From 52d5ff5dc54c40925f3ec2eecc8573cb5e69d830 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomi=20Korpip=C3=A4=C3=A4?= Date: Wed, 18 Jun 2014 06:44:42 +0300 Subject: Bars: Moved bar, background and grid line drawing to separate methods + Added floor reflection support in ifdefs Change-Id: I1060aee1bc8f7a0360ad01675c36c0f1a2120f1c Reviewed-by: Miikka Heikkinen --- .../engine/abstract3drenderer.cpp | 42 ++- .../engine/abstract3drenderer_p.h | 10 + src/datavisualization/engine/bars3drenderer.cpp | 383 +++++++++++++++------ src/datavisualization/engine/bars3drenderer_p.h | 23 ++ src/datavisualization/engine/shaders/default.frag | 2 +- .../engine/shaders/default_ES2.frag | 2 +- .../engine/shaders/shadowNoTex.frag | 2 +- 7 files changed, 344 insertions(+), 120 deletions(-) diff --git a/src/datavisualization/engine/abstract3drenderer.cpp b/src/datavisualization/engine/abstract3drenderer.cpp index 04ede782..132a0514 100644 --- a/src/datavisualization/engine/abstract3drenderer.cpp +++ b/src/datavisualization/engine/abstract3drenderer.cpp @@ -137,7 +137,7 @@ void Abstract3DRenderer::render(const GLuint defaultFboHandle) glEnable(GL_SCISSOR_TEST); QVector4D clearColor = Utils::vectorFromColor(m_cachedTheme->windowColor()); glClearColor(clearColor.x(), clearColor.y(), clearColor.z(), 1.0f); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); glDisable(GL_SCISSOR_TEST); } @@ -983,6 +983,16 @@ void Abstract3DRenderer::updateCustomItemPositions() } } +#ifdef USE_REFLECTIONS +void Abstract3DRenderer::drawCustomItems(RenderingState state, + ShaderHelper *shader, + const QMatrix4x4 &viewMatrix, + const QMatrix4x4 &projectionViewMatrix, + const QMatrix4x4 &depthProjectionViewMatrix, + GLuint depthTexture, + GLfloat shadowQuality, + GLfloat reflection) +#else void Abstract3DRenderer::drawCustomItems(RenderingState state, ShaderHelper *shader, const QMatrix4x4 &viewMatrix, @@ -990,6 +1000,7 @@ void Abstract3DRenderer::drawCustomItems(RenderingState state, const QMatrix4x4 &depthProjectionViewMatrix, GLuint depthTexture, GLfloat shadowQuality) +#endif { if (m_customRenderCache.isEmpty()) return; @@ -1001,8 +1012,6 @@ void Abstract3DRenderer::drawCustomItems(RenderingState state, shader->setUniformValue(shader->lightColor(), Utils::vectorFromColor(m_cachedTheme->lightColor())); shader->setUniformValue(shader->view(), viewMatrix); - - glEnable(GL_TEXTURE_2D); } // Draw custom items @@ -1035,10 +1044,36 @@ void Abstract3DRenderer::drawCustomItems(RenderingState state, * QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, -camRotationY); } +#ifdef USE_REFLECTIONS + if (reflection < 0.0f) { + if (item->itemPointer()->d_ptr->m_isLabelItem) + continue; + else + glCullFace(GL_FRONT); + } else { + glCullFace(GL_BACK); + } + QVector3D trans = item->translation(); + trans.setY(reflection * trans.y()); + modelMatrix.translate(trans); + if (reflection < 0.0f) { + QQuaternion mirror = QQuaternion(rotation.scalar(), + -rotation.x(), rotation.y(), -rotation.z()); + modelMatrix.rotate(mirror); + itModelMatrix.rotate(mirror); + } else { + modelMatrix.rotate(rotation); + itModelMatrix.rotate(rotation); + } + QVector3D scale = item->scaling(); + scale.setY(reflection * scale.y()); + modelMatrix.scale(scale); +#else modelMatrix.translate(item->translation()); modelMatrix.rotate(rotation); modelMatrix.scale(item->scaling()); itModelMatrix.rotate(rotation); +#endif if (!item->isFacingCamera()) itModelMatrix.scale(item->scaling()); MVPMatrix = projectionViewMatrix * modelMatrix; @@ -1091,7 +1126,6 @@ void Abstract3DRenderer::drawCustomItems(RenderingState state, } if (RenderingNormal == state) { - glDisable(GL_TEXTURE_2D); glDisable(GL_BLEND); glEnable(GL_CULL_FACE); } diff --git a/src/datavisualization/engine/abstract3drenderer_p.h b/src/datavisualization/engine/abstract3drenderer_p.h index 0dfc7367..325ac8e6 100644 --- a/src/datavisualization/engine/abstract3drenderer_p.h +++ b/src/datavisualization/engine/abstract3drenderer_p.h @@ -29,6 +29,8 @@ #ifndef ABSTRACT3DRENDERER_P_H #define ABSTRACT3DRENDERER_P_H +//#define USE_REFLECTIONS // Bars only test version (only floor reflects) + #include #include "datavisualizationglobal_p.h" @@ -139,11 +141,19 @@ public: void setSelectionLabel(const QString &label); QString &selectionLabel(); +#ifdef USE_REFLECTIONS + void drawCustomItems(RenderingState state, ShaderHelper *shader, + const QMatrix4x4 &viewMatrix, + const QMatrix4x4 &projectionViewMatrix, + const QMatrix4x4 &depthProjectionViewMatrix, + GLuint depthTexture, GLfloat shadowQuality, GLfloat reflection = 1.0f); +#else void drawCustomItems(RenderingState state, ShaderHelper *shader, const QMatrix4x4 &viewMatrix, const QMatrix4x4 &projectionViewMatrix, const QMatrix4x4 &depthProjectionViewMatrix, GLuint depthTexture, GLfloat shadowQuality); +#endif QVector4D indexToSelectionColor(GLint index); signals: diff --git a/src/datavisualization/engine/bars3drenderer.cpp b/src/datavisualization/engine/bars3drenderer.cpp index 689f3f5d..f8750b17 100644 --- a/src/datavisualization/engine/bars3drenderer.cpp +++ b/src/datavisualization/engine/bars3drenderer.cpp @@ -564,7 +564,6 @@ void Bars3DRenderer::drawSlicedScene() if (sliceGridLabels) { // Bind label shader m_labelShader->bind(); - glEnable(GL_TEXTURE_2D); glCullFace(GL_BACK); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); @@ -587,7 +586,6 @@ void Bars3DRenderer::drawSlicedScene() } labelNbr++; } - glDisable(GL_TEXTURE_2D); glDisable(GL_BLEND); glEnable(GL_DEPTH_TEST); } @@ -760,7 +758,6 @@ void Bars3DRenderer::drawSlicedScene() // Draw labels m_labelShader->bind(); glDisable(GL_DEPTH_TEST); - glEnable(GL_TEXTURE_2D); glCullFace(GL_BACK); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); @@ -889,7 +886,6 @@ void Bars3DRenderer::drawSlicedScene() m_cachedSelectionMode, m_labelShader, m_labelObj, activeCamera, false, false, Drawer::LabelMid, Qt::AlignBottom); - glDisable(GL_TEXTURE_2D); glDisable(GL_BLEND); glEnable(GL_DEPTH_TEST); @@ -990,8 +986,6 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) QMatrix4x4 projectionViewMatrix = projectionMatrix * viewMatrix; - bool rowMode = m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionRow); - GLfloat rowScaleFactor = m_rowWidth / m_scaleFactor; GLfloat columnScaleFactor = m_columnDepth / m_scaleFactor; @@ -1052,6 +1046,12 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) shadowOffset = -0.015f; } +#ifdef USE_REFLECTIONS + if (m_yFlipped && item.height() > 0.0 + || !m_yFlipped && item.height() < 0.0) { + continue; + } +#endif QMatrix4x4 modelMatrix; QMatrix4x4 MVPMatrix; @@ -1204,8 +1204,142 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) m_primarySubViewport.height()); } - // Enable texturing - glEnable(GL_TEXTURE_2D); +#ifdef USE_REFLECTIONS + // + // Draw reflections + // + glDisable(GL_DEPTH_TEST); + glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); + glEnable(GL_STENCIL_TEST); + glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE); + glStencilFunc(GL_ALWAYS, 1, 0xffffffff); + + // Draw background stencil + drawBackground(rowScaleFactor, columnScaleFactor, backgroundRotation, + depthProjectionViewMatrix, projectionViewMatrix, viewMatrix); + + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + glEnable(GL_DEPTH_TEST); + + glStencilFunc(GL_EQUAL, 1, 0xffffffff); + glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); + + // Set light + QVector3D reflectionLightPos = lightPos; + reflectionLightPos.setY(-(lightPos.y())); + m_cachedScene->activeLight()->setPosition(reflectionLightPos); + + // Draw bar reflections + (void)drawBars(&selectedBar, depthProjectionViewMatrix, + projectionViewMatrix, viewMatrix, + startRow, stopRow, stepRow, + startBar, stopBar, stepBar, -1.0f); + + Abstract3DRenderer::drawCustomItems(RenderingNormal, m_customItemShader, viewMatrix, + projectionViewMatrix, depthProjectionViewMatrix, + m_depthTexture, m_shadowQualityToShader, -1.0f); + + // Reset light + m_cachedScene->activeLight()->setPosition(lightPos); + + glDisable(GL_STENCIL_TEST); + + glCullFace(GL_BACK); +#endif + + // + // Draw the real scene + // + // Draw background +#ifdef USE_REFLECTIONS + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + drawBackground(rowScaleFactor, columnScaleFactor, backgroundRotation, + depthProjectionViewMatrix, projectionViewMatrix, viewMatrix, 0.5f); + glDisable(GL_BLEND); +#else + drawBackground(rowScaleFactor, columnScaleFactor, backgroundRotation, + depthProjectionViewMatrix, projectionViewMatrix, viewMatrix); +#endif + + // Draw bars + bool barSelectionFound = drawBars(&selectedBar, depthProjectionViewMatrix, + projectionViewMatrix, viewMatrix, + startRow, stopRow, stepRow, + startBar, stopBar, stepBar); + + // Draw grid lines + drawGridLines(rowScaleFactor, columnScaleFactor, depthProjectionViewMatrix, + projectionViewMatrix, viewMatrix); + + // Draw custom items + Abstract3DRenderer::drawCustomItems(RenderingNormal, m_customItemShader, viewMatrix, + projectionViewMatrix, depthProjectionViewMatrix, + m_depthTexture, m_shadowQualityToShader); + + // Draw labels + drawLabels(false, activeCamera, viewMatrix, projectionMatrix, rowScaleFactor, + columnScaleFactor); + + // Handle selected bar label generation + if (barSelectionFound) { + // Print value of selected bar + glDisable(GL_DEPTH_TEST); + // Draw the selection label + LabelItem &labelItem = selectionLabelItem(); + if (m_selectedBar != selectedBar || m_updateLabels || !labelItem.textureId() + || m_selectionLabelDirty) { + QString labelText = selectionLabel(); + if (labelText.isNull() || m_selectionLabelDirty) { + labelText = m_selectedSeriesCache->itemLabel(); + setSelectionLabel(labelText); + m_selectionLabelDirty = false; + } + m_drawer->generateLabelItem(labelItem, labelText); + m_selectedBar = selectedBar; + } + + Drawer::LabelPosition position = + m_selectedBar->height() >= 0 ? Drawer::LabelOver : Drawer::LabelBelow; + + m_drawer->drawLabel(*selectedBar, labelItem, viewMatrix, projectionMatrix, + zeroVector, identityQuaternion, selectedBar->height(), + m_cachedSelectionMode, m_labelShader, + m_labelObj, activeCamera, true, false, position); + + // Reset label update flag; they should have been updated when we get here + m_updateLabels = false; + + glEnable(GL_DEPTH_TEST); + } else { + m_selectedBar = 0; + } + + glDisable(GL_BLEND); + + // Release shader + glUseProgram(0); + m_selectionDirty = false; +} + +#ifdef USE_REFLECTIONS +bool Bars3DRenderer::drawBars(BarRenderItem **selectedBar, + const QMatrix4x4 &depthProjectionViewMatrix, + const QMatrix4x4 &projectionViewMatrix, const QMatrix4x4 &viewMatrix, + GLint startRow, GLint stopRow, GLint stepRow, + GLint startBar, GLint stopBar, GLint stepBar, GLfloat reflection) +#else +bool Bars3DRenderer::drawBars(BarRenderItem **selectedBar, + const QMatrix4x4 &depthProjectionViewMatrix, + const QMatrix4x4 &projectionViewMatrix, const QMatrix4x4 &viewMatrix, + GLint startRow, GLint stopRow, GLint stepRow, + GLint startBar, GLint stopBar, GLint stepBar) +#endif +{ + QVector3D lightPos = m_cachedScene->activeLight()->position(); + QVector4D lightColor = Utils::vectorFromColor(m_cachedTheme->lightColor()); + + bool rowMode = m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionRow); ShaderHelper *barShader = 0; GLuint gradientTexture = 0; @@ -1251,7 +1385,6 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) m_sliceTitleItem = 0; } - // Draw bars glEnable(GL_POLYGON_OFFSET_FILL); glPolygonOffset(0.5f, 1.0f); @@ -1302,12 +1435,15 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) } previousColorStyle = colorStyle; - for (int row = startRow; row != stopRow; row += stepRow) { BarRenderItemRow &renderRow = renderArray[row]; for (int bar = startBar; bar != stopBar; bar += stepBar) { BarRenderItem &item = renderRow[bar]; +#ifdef USE_REFLECTIONS + if (reflection * item.height() < 0) +#else if (item.height() < 0) +#endif glCullFace(GL_FRONT); else glCullFace(GL_BACK); @@ -1316,13 +1452,20 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) QMatrix4x4 itModelMatrix; QMatrix4x4 MVPMatrix; - colPos = (bar + seriesPos) * (m_cachedBarSpacing.width()); - rowPos = (row + 0.5f) * (m_cachedBarSpacing.height()); + GLfloat colPos = (bar + seriesPos) * (m_cachedBarSpacing.width()); + GLfloat rowPos = (row + 0.5f) * (m_cachedBarSpacing.height()); +#ifdef USE_REFLECTIONS + modelMatrix.translate((colPos - m_rowWidth) / m_scaleFactor, + reflection * item.height(), + (m_columnDepth - rowPos) / m_scaleFactor); + modelScaler.setY(reflection * item.height()); +#else modelMatrix.translate((colPos - m_rowWidth) / m_scaleFactor, item.height(), (m_columnDepth - rowPos) / m_scaleFactor); modelScaler.setY(item.height()); +#endif if (!seriesRotation.isIdentity() || !item.rotation().isIdentity()) { QQuaternion totalRotation = seriesRotation * item.rotation(); modelMatrix.rotate(totalRotation); @@ -1357,8 +1500,8 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) // We have no ownership, don't delete the previous one if (!m_cachedIsSlicingActivated && m_selectedSeriesCache == cache) { - selectedBar = &item; - selectedBar->setPosition(QPoint(row, bar)); + *selectedBar = &item; + (*selectedBar)->setPosition(QPoint(row, bar)); item.setTranslation(modelMatrix.column(3).toVector3D()); barSelectionFound = true; } @@ -1438,8 +1581,16 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) } } +#ifdef USE_REFLECTIONS + // Skip drawing of 0-height bars and reflections of bars on the "wrong side" + if (item.height() != 0 + && (reflection == 1.0f || (reflection != 1.0f + && (m_yFlipped && item.height() < 0.0) + || (!m_yFlipped && item.height() > 0.0)))) { +#else // Skip drawing of 0-height bars if (item.height() != 0) { +#endif // Set shader bindings barShader->setUniformValue(barShader->model(), modelMatrix); barShader->setUniformValue(barShader->nModel(), @@ -1453,7 +1604,12 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) } #if !defined(QT_OPENGL_ES_2) +#ifdef USE_REFLECTIONS + if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone + && reflection == 1.0f) { +#else if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { +#endif // Set shadow shader bindings QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix; barShader->setUniformValue(barShader->shadowQ(), @@ -1468,10 +1624,20 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) } else #else Q_UNUSED(shadowLightStrength); + Q_UNUSED(depthProjectionViewMatrix); #endif { // Set shadowless shader bindings - barShader->setUniformValue(barShader->lightS(), lightStrength); +#ifdef USE_REFLECTIONS + if (reflection != 1.0f + && m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { + barShader->setUniformValue(barShader->lightS(), + adjustedLightStrength); + } else +#endif + { + barShader->setUniformValue(barShader->lightS(), lightStrength); + } // Draw the object m_drawer->drawObject(barShader, barObj, gradientTexture); @@ -1481,12 +1647,32 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) } } } - glDisable(GL_POLYGON_OFFSET_FILL); // Reset culling glCullFace(GL_BACK); + return barSelectionFound; +} + +#ifdef USE_REFLECTIONS +void Bars3DRenderer::drawBackground(GLfloat rowScaleFactor, GLfloat columnScaleFactor, + GLfloat backgroundRotation, + const QMatrix4x4 &depthProjectionViewMatrix, + const QMatrix4x4 &projectionViewMatrix, + const QMatrix4x4 &viewMatrix, GLfloat reflection) +#else +void Bars3DRenderer::drawBackground(GLfloat rowScaleFactor, GLfloat columnScaleFactor, + GLfloat backgroundRotation, + const QMatrix4x4 &depthProjectionViewMatrix, + const QMatrix4x4 &projectionViewMatrix, + const QMatrix4x4 &viewMatrix) +#endif +{ + QVector3D lightPos = m_cachedScene->activeLight()->position(); + QVector4D lightColor = Utils::vectorFromColor(m_cachedTheme->lightColor()); + GLfloat adjustedLightStrength = m_cachedTheme->lightStrength() / 10.0f; + // Bind background shader m_backgroundShader->bind(); @@ -1497,59 +1683,20 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) QMatrix4x4 itModelMatrix; QVector3D backgroundScaler(rowScaleFactor, 1.0f, columnScaleFactor); - modelMatrix.translate(0.0f, m_backgroundAdjustment, 0.0f); - - modelMatrix.scale(backgroundScaler); - itModelMatrix.scale(backgroundScaler); - modelMatrix.rotate(backgroundRotation, 0.0f, 1.0f, 0.0f); - itModelMatrix.rotate(backgroundRotation, 0.0f, 1.0f, 0.0f); - -#ifdef SHOW_DEPTH_TEXTURE_SCENE - MVPMatrix = depthProjectionViewMatrix * modelMatrix; -#else - MVPMatrix = projectionViewMatrix * modelMatrix; -#endif QVector4D backgroundColor = Utils::vectorFromColor(m_cachedTheme->backgroundColor()); +#ifdef USE_REFLECTIONS + backgroundColor.setW(backgroundColor.w() * reflection); +#endif // Set shader bindings m_backgroundShader->setUniformValue(m_backgroundShader->lightP(), lightPos); m_backgroundShader->setUniformValue(m_backgroundShader->view(), viewMatrix); - m_backgroundShader->setUniformValue(m_backgroundShader->model(), modelMatrix); - m_backgroundShader->setUniformValue(m_backgroundShader->nModel(), - itModelMatrix.inverted().transposed()); - m_backgroundShader->setUniformValue(m_backgroundShader->MVP(), MVPMatrix); m_backgroundShader->setUniformValue(m_backgroundShader->color(), backgroundColor); m_backgroundShader->setUniformValue(m_backgroundShader->ambientS(), m_cachedTheme->ambientLightStrength() * 2.0f); m_backgroundShader->setUniformValue(m_backgroundShader->lightColor(), lightColor); -#if !defined(QT_OPENGL_ES_2) - if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { - // Set shadow shader bindings - QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix; - m_backgroundShader->setUniformValue(m_backgroundShader->shadowQ(), - m_shadowQualityToShader); - m_backgroundShader->setUniformValue(m_backgroundShader->depth(), depthMVPMatrix); - m_backgroundShader->setUniformValue(m_backgroundShader->lightS(), - adjustedLightStrength); - - // Draw the object - m_drawer->drawObject(m_backgroundShader, m_backgroundObj, 0, m_depthTexture); - } else -#endif - { - // Set shadowless shader bindings - m_backgroundShader->setUniformValue(m_backgroundShader->lightS(), - m_cachedTheme->lightStrength()); - - // Draw the object - m_drawer->drawObject(m_backgroundShader, m_backgroundObj); - } - // Draw floor - modelMatrix = QMatrix4x4(); - itModelMatrix = QMatrix4x4(); - modelMatrix.scale(backgroundScaler); if (m_yFlipped) @@ -1583,20 +1730,79 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) // Draw the object m_drawer->drawObject(m_backgroundShader, m_gridLineObj); } - } - // Disable textures - glDisable(GL_TEXTURE_2D); + // Draw walls + modelMatrix = QMatrix4x4(); + itModelMatrix = QMatrix4x4(); + modelMatrix.translate(0.0f, m_backgroundAdjustment, 0.0f); - // Draw grid lines + modelMatrix.scale(backgroundScaler); + itModelMatrix.scale(backgroundScaler); + modelMatrix.rotate(backgroundRotation, 0.0f, 1.0f, 0.0f); + itModelMatrix.rotate(backgroundRotation, 0.0f, 1.0f, 0.0f); + +#ifdef SHOW_DEPTH_TEXTURE_SCENE + MVPMatrix = depthProjectionViewMatrix * modelMatrix; +#else + MVPMatrix = projectionViewMatrix * modelMatrix; +#endif + + // Set changed shader bindings + m_backgroundShader->setUniformValue(m_backgroundShader->model(), modelMatrix); + m_backgroundShader->setUniformValue(m_backgroundShader->nModel(), + itModelMatrix.inverted().transposed()); + m_backgroundShader->setUniformValue(m_backgroundShader->MVP(), MVPMatrix); +#ifdef USE_REFLECTIONS + if (reflection != 1.0f) { +#endif +#if !defined(QT_OPENGL_ES_2) + if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { + // Set shadow shader bindings + QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix; + m_backgroundShader->setUniformValue(m_backgroundShader->shadowQ(), + m_shadowQualityToShader); + m_backgroundShader->setUniformValue(m_backgroundShader->depth(), depthMVPMatrix); + m_backgroundShader->setUniformValue(m_backgroundShader->lightS(), + adjustedLightStrength); + + // Draw the object + m_drawer->drawObject(m_backgroundShader, m_backgroundObj, 0, m_depthTexture); + } else +#else + Q_UNUSED(adjustedLightStrength); + Q_UNUSED(depthProjectionViewMatrix); +#endif + { + // Set shadowless shader bindings + m_backgroundShader->setUniformValue(m_backgroundShader->lightS(), + m_cachedTheme->lightStrength()); + + // Draw the object + m_drawer->drawObject(m_backgroundShader, m_backgroundObj); + } +#ifdef USE_REFLECTIONS + } +#endif + } +} + +void Bars3DRenderer::drawGridLines(GLfloat rowScaleFactor, GLfloat columnScaleFactor, + const QMatrix4x4 &depthProjectionViewMatrix, + const QMatrix4x4 &projectionViewMatrix, + const QMatrix4x4 &viewMatrix) +{ if (m_cachedTheme->isGridEnabled()) { #if !(defined QT_OPENGL_ES_2) ShaderHelper *lineShader = m_backgroundShader; #else + Q_UNUSED(depthProjectionViewMatrix); ShaderHelper *lineShader = m_selectionShader; // Plain color shader for GL_LINES #endif QQuaternion lineRotation; + QVector3D lightPos = m_cachedScene->activeLight()->position(); + QVector4D lightColor = Utils::vectorFromColor(m_cachedTheme->lightColor()); + // Bind bar shader lineShader->bind(); @@ -1638,7 +1844,7 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) QMatrix4x4 MVPMatrix; QMatrix4x4 itModelMatrix; - rowPos = row * m_cachedBarSpacing.height(); + GLfloat rowPos = row * m_cachedBarSpacing.height(); modelMatrix.translate(0.0f, yFloorLinePosition, (m_columnDepth - rowPos) / m_scaleFactor); modelMatrix.scale(gridLineScaler); @@ -1680,7 +1886,7 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) QMatrix4x4 MVPMatrix; QMatrix4x4 itModelMatrix; - colPos = bar * m_cachedBarSpacing.width(); + GLfloat colPos = bar * m_cachedBarSpacing.width(); modelMatrix.translate((m_rowWidth - colPos) / m_scaleFactor, yFloorLinePosition, 0.0f); modelMatrix.scale(gridLineScaler); @@ -1809,54 +2015,6 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) } } } - - Abstract3DRenderer::drawCustomItems(RenderingNormal, m_customItemShader, viewMatrix, - projectionViewMatrix, depthProjectionViewMatrix, - m_depthTexture, m_shadowQualityToShader); - - drawLabels(false, activeCamera, viewMatrix, projectionMatrix, rowScaleFactor, - columnScaleFactor); - - // Handle selected bar label generation - if (barSelectionFound) { - // Print value of selected bar - glDisable(GL_DEPTH_TEST); - // Draw the selection label - LabelItem &labelItem = selectionLabelItem(); - if (m_selectedBar != selectedBar || m_updateLabels || !labelItem.textureId() - || m_selectionLabelDirty) { - QString labelText = selectionLabel(); - if (labelText.isNull() || m_selectionLabelDirty) { - labelText = m_selectedSeriesCache->itemLabel(); - setSelectionLabel(labelText); - m_selectionLabelDirty = false; - } - m_drawer->generateLabelItem(labelItem, labelText); - m_selectedBar = selectedBar; - } - - Drawer::LabelPosition position = - m_selectedBar->height() >= 0 ? Drawer::LabelOver : Drawer::LabelBelow; - - m_drawer->drawLabel(*selectedBar, labelItem, viewMatrix, projectionMatrix, - zeroVector, identityQuaternion, selectedBar->height(), - m_cachedSelectionMode, m_labelShader, - m_labelObj, activeCamera, true, false, position); - - // Reset label update flag; they should have been updated when we get here - m_updateLabels = false; - - glEnable(GL_DEPTH_TEST); - } else { - m_selectedBar = 0; - } - - glDisable(GL_TEXTURE_2D); - glDisable(GL_BLEND); - - // Release shader - glUseProgram(0); - m_selectionDirty = false; } void Bars3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCamera, @@ -1873,7 +2031,6 @@ void Bars3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCamer shader = m_labelShader; shader->bind(); - glEnable(GL_TEXTURE_2D); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); } diff --git a/src/datavisualization/engine/bars3drenderer_p.h b/src/datavisualization/engine/bars3drenderer_p.h index 3a0ab3b8..29835741 100644 --- a/src/datavisualization/engine/bars3drenderer_p.h +++ b/src/datavisualization/engine/bars3drenderer_p.h @@ -149,6 +149,29 @@ private: const QMatrix4x4 &viewMatrix, const QMatrix4x4 &projectionMatrix, GLfloat rowScaleFactor, GLfloat columnScaleFactor); +#ifdef USE_REFLECTIONS + bool drawBars(BarRenderItem **selectedBar, const QMatrix4x4 &depthProjectionViewMatrix, + const QMatrix4x4 &projectionViewMatrix, const QMatrix4x4 &viewMatrix, + GLint startRow, GLint stopRow, GLint stepRow, + GLint startBar, GLint stopBar, GLint stepBar, GLfloat reflection = 1.0f); + void drawBackground(GLfloat rowScaleFactor, GLfloat columnScaleFactor, + GLfloat backgroundRotation, const QMatrix4x4 &depthProjectionViewMatrix, + const QMatrix4x4 &projectionViewMatrix, const QMatrix4x4 &viewMatrix, + GLfloat reflection = 1.0f); +#else + bool drawBars(BarRenderItem **selectedBar, const QMatrix4x4 &depthProjectionViewMatrix, + const QMatrix4x4 &projectionViewMatrix, const QMatrix4x4 &viewMatrix, + GLint startRow, GLint stopRow, GLint stepRow, + GLint startBar, GLint stopBar, GLint stepBar); + void drawBackground(GLfloat rowScaleFactor, GLfloat columnScaleFactor, + GLfloat backgroundRotation, const QMatrix4x4 &depthProjectionViewMatrix, + const QMatrix4x4 &projectionViewMatrix, const QMatrix4x4 &viewMatrix); +#endif + void drawGridLines(GLfloat rowScaleFactor, GLfloat columnScaleFactor, + const QMatrix4x4 &depthProjectionViewMatrix, + const QMatrix4x4 &projectionViewMatrix, + const QMatrix4x4 &viewMatrix); + void loadBackgroundMesh(); void initSelectionShader(); void initBackgroundShaders(const QString &vertexShader, const QString &fragmentShader); diff --git a/src/datavisualization/engine/shaders/default.frag b/src/datavisualization/engine/shaders/default.frag index d16055a3..c03b1054 100644 --- a/src/datavisualization/engine/shaders/default.frag +++ b/src/datavisualization/engine/shaders/default.frag @@ -32,6 +32,6 @@ void main() { materialAmbientColor + materialDiffuseColor * lightStrength * pow(cosTheta, 2) / distance + materialSpecularColor * lightStrength * pow(cosAlpha, 5) / distance; - gl_FragColor.a = 1.0; + gl_FragColor.a = color_mdl.a; } diff --git a/src/datavisualization/engine/shaders/default_ES2.frag b/src/datavisualization/engine/shaders/default_ES2.frag index 73d66d5b..60fa3c43 100644 --- a/src/datavisualization/engine/shaders/default_ES2.frag +++ b/src/datavisualization/engine/shaders/default_ES2.frag @@ -34,6 +34,6 @@ void main() { materialAmbientColor + materialDiffuseColor * lightStrength * (cosTheta * cosTheta) / distance + materialSpecularColor * lightStrength * (cosAlpha * cosAlpha * cosAlpha * cosAlpha * cosAlpha) / distance; - gl_FragColor.a = 1.0; + gl_FragColor.a = color_mdl.a; } diff --git a/src/datavisualization/engine/shaders/shadowNoTex.frag b/src/datavisualization/engine/shaders/shadowNoTex.frag index b2e7adfc..84e2f209 100644 --- a/src/datavisualization/engine/shaders/shadowNoTex.frag +++ b/src/datavisualization/engine/shaders/shadowNoTex.frag @@ -61,6 +61,6 @@ void main() { (materialAmbientColor + materialDiffuseColor * lightStrength * cosTheta + materialSpecularColor * lightStrength * pow(cosAlpha, 10)); - gl_FragColor.a = 1.0; + gl_FragColor.a = color_mdl.a; gl_FragColor.rgb = visibility * clamp(gl_FragColor.rgb, 0.0, 1.0); } -- cgit v1.2.3 From b86c799f7758f64e781ebf97a2e660675db8168c Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Wed, 18 Jun 2014 12:38:15 +0300 Subject: Fix label width issue when axis range changes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Task-number: QTRD-3177 Change-Id: If76ddb47034f6af452ca58f4f792a1c31771110f Reviewed-by: Tomi Korpipää --- src/datavisualization/engine/axisrendercache.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/datavisualization/engine/axisrendercache.cpp b/src/datavisualization/engine/axisrendercache.cpp index 0b7a5c7a..6fdb84c6 100644 --- a/src/datavisualization/engine/axisrendercache.cpp +++ b/src/datavisualization/engine/axisrendercache.cpp @@ -106,10 +106,12 @@ void AxisRenderCache::setLabels(const QStringList &labels) if (i >= oldSize) m_labelItems.append(new LabelItem); if (m_drawer) { - if (labels.at(i).isEmpty()) + if (labels.at(i).isEmpty()) { m_labelItems[i]->clear(); - else if (i >= oldSize || labels.at(i) != m_labels.at(i)) + } else if (i >= oldSize || labels.at(i) != m_labels.at(i) + || m_labelItems[i]->size().width() != widest) { m_drawer->generateLabelItem(*m_labelItems[i], labels.at(i), widest); + } } } m_labels = labels; -- cgit v1.2.3 From 03baf7bc0b3bf07625e1111fe50c5262047ee302 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Mon, 16 Jun 2014 09:26:26 +0300 Subject: Added some extra checking for selection texture creation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: Iedeaa2581c32b6d46168568fef736b234ac17e95 Reviewed-by: Tomi Korpipää --- src/datavisualization/engine/bars3drenderer.cpp | 3 ++- src/datavisualization/engine/scatter3drenderer.cpp | 3 ++- src/datavisualization/engine/surface3drenderer.cpp | 13 +++++++----- src/datavisualization/utils/texturehelper.cpp | 23 +++++++++++++++++----- 4 files changed, 30 insertions(+), 12 deletions(-) diff --git a/src/datavisualization/engine/bars3drenderer.cpp b/src/datavisualization/engine/bars3drenderer.cpp index f8750b17..f909c7d6 100644 --- a/src/datavisualization/engine/bars3drenderer.cpp +++ b/src/datavisualization/engine/bars3drenderer.cpp @@ -1117,7 +1117,8 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) // Skip selection mode drawing if we're slicing or have no selection mode if (!m_cachedIsSlicingActivated && m_cachedSelectionMode > QAbstract3DGraph::SelectionNone && m_selectionState == SelectOnScene - && (m_visibleSeriesCount > 0 || !m_customRenderCache.isEmpty())) { + && (m_visibleSeriesCount > 0 || !m_customRenderCache.isEmpty()) + && m_selectionTexture) { // Bind selection shader m_selectionShader->bind(); diff --git a/src/datavisualization/engine/scatter3drenderer.cpp b/src/datavisualization/engine/scatter3drenderer.cpp index dd50188b..7ac81552 100644 --- a/src/datavisualization/engine/scatter3drenderer.cpp +++ b/src/datavisualization/engine/scatter3drenderer.cpp @@ -551,7 +551,8 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) // Skip selection mode drawing if we have no selection mode if (m_cachedSelectionMode > QAbstract3DGraph::SelectionNone && SelectOnScene == m_selectionState - && (m_visibleSeriesCount > 0 || !m_customRenderCache.isEmpty())) { + && (m_visibleSeriesCount > 0 || !m_customRenderCache.isEmpty()) + && m_selectionTexture) { // Draw dots to selection buffer glBindFramebuffer(GL_FRAMEBUFFER, m_selectionFrameBuffer); glViewport(0, 0, diff --git a/src/datavisualization/engine/surface3drenderer.cpp b/src/datavisualization/engine/surface3drenderer.cpp index 38c8d8fe..60e57529 100644 --- a/src/datavisualization/engine/surface3drenderer.cpp +++ b/src/datavisualization/engine/surface3drenderer.cpp @@ -1190,7 +1190,8 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) if (!m_cachedIsSlicingActivated && (!m_renderCacheList.isEmpty() || !m_customRenderCache.isEmpty()) && m_selectionState == SelectOnScene - && m_cachedSelectionMode > QAbstract3DGraph::SelectionNone) { + && m_cachedSelectionMode > QAbstract3DGraph::SelectionNone + && m_selectionResultTexture) { m_selectionShader->bind(); glBindFramebuffer(GL_FRAMEBUFFER, m_selectionFrameBuffer); glViewport(0, @@ -2690,10 +2691,12 @@ void Surface3DRenderer::updateDepthBuffer() m_depthTexture = m_textureHelper->createDepthTextureFrameBuffer(m_primarySubViewport.size(), m_depthFrameBuffer, m_shadowQualityMultiplier); - m_textureHelper->fillDepthTexture(m_depthTexture, m_primarySubViewport.size(), - m_shadowQualityMultiplier, 1.0f); - m_depthModelTexture = m_textureHelper->createDepthTexture(m_primarySubViewport.size(), - m_shadowQualityMultiplier); + if (m_depthTexture) { + m_textureHelper->fillDepthTexture(m_depthTexture, m_primarySubViewport.size(), + m_shadowQualityMultiplier, 1.0f); + m_depthModelTexture = m_textureHelper->createDepthTexture(m_primarySubViewport.size(), + m_shadowQualityMultiplier); + } if (!m_depthTexture || !m_depthModelTexture) lowerShadowQuality(); } diff --git a/src/datavisualization/utils/texturehelper.cpp b/src/datavisualization/utils/texturehelper.cpp index 2a2a89dd..97f9c672 100644 --- a/src/datavisualization/utils/texturehelper.cpp +++ b/src/datavisualization/utils/texturehelper.cpp @@ -116,11 +116,22 @@ GLuint TextureHelper::createSelectionTexture(const QSize &size, GLuint &frameBuf if (!depthBuffer) glGenRenderbuffers(1, &depthBuffer); glBindRenderbuffer(GL_RENDERBUFFER, depthBuffer); + GLenum status = glGetError(); + // glGetError docs advise to call glGetError in loop to clear all error flags + while (status) + status = glGetError(); #if !defined(QT_OPENGL_ES_2) glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, size.width(), size.height()); #else glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, size.width(), size.height()); #endif + status = glGetError(); + if (status) { + qCritical() << "Selection texture render buffer creation failed:" << status; + glDeleteTextures(1, &textureid); + glBindRenderbuffer(GL_RENDERBUFFER, 0); + return 0; + } glBindRenderbuffer(GL_RENDERBUFFER, 0); // Create frame buffer @@ -134,10 +145,11 @@ GLuint TextureHelper::createSelectionTexture(const QSize &size, GLuint &frameBuf glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthBuffer); // Verify that the frame buffer is complete - GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + status = glCheckFramebufferStatus(GL_FRAMEBUFFER); if (status != GL_FRAMEBUFFER_COMPLETE) { - qCritical() << "Frame buffer creation failed" << status; - return 0; + qCritical() << "Selection texture frame buffer creation failed:" << status; + glDeleteTextures(1, &textureid); + textureid = 0; } // Restore the default framebuffer @@ -212,8 +224,9 @@ GLuint TextureHelper::createDepthTextureFrameBuffer(const QSize &size, GLuint &f // Verify that the frame buffer is complete GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); if (status != GL_FRAMEBUFFER_COMPLETE) { - qCritical() << "Frame buffer creation failed" << status; - return 0; + qCritical() << "Depth texture frame buffer creation failed" << status; + glDeleteTextures(1, &depthtextureid); + depthtextureid = 0; } // Restore the default framebuffer -- cgit v1.2.3 From ec195a34594dea6145af5e8f2fedc2f9401d0f14 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Thu, 19 Jun 2014 09:54:03 +0300 Subject: Polar graph support, phase one - Polar property for toggling the polar mode - Example added. Example docs will be added in another patch once all of the functionality the example needs has been implemented. - Only surface graph supports polar so far. Scatter to be added later. Change-Id: I54d36f764ac1771ac88f73a5f3a2142f2309f6e8 Reviewed-by: Mika Salmela --- examples/datavisualization/datavisualization.pro | 3 +- .../qmlspectrogram/doc/src/qmlspectrogram.qdoc | 41 + examples/datavisualization/qmlspectrogram/main.cpp | 42 + .../qmlspectrogram/qml/qmlspectrogram/Data.qml | 1560 ++++++++++++++++++++ .../qml/qmlspectrogram/NewButton.qml | 52 + .../qmlspectrogram/qml/qmlspectrogram/main.qml | 239 +++ .../qmlspectrogram/qmlspectrogram.pro | 12 + .../qmlspectrogram/qmlspectrogram.qrc | 7 + ...tdatavisualization-qml-abstractdeclarative.qdoc | 13 + .../engine/abstract3dcontroller.cpp | 22 + .../engine/abstract3dcontroller_p.h | 26 +- .../engine/abstract3drenderer.cpp | 152 +- .../engine/abstract3drenderer_p.h | 17 + src/datavisualization/engine/bars3drenderer.cpp | 23 +- src/datavisualization/engine/bars3drenderer_p.h | 1 - src/datavisualization/engine/qabstract3dgraph.cpp | 33 +- src/datavisualization/engine/qabstract3dgraph.h | 5 + src/datavisualization/engine/scatter3drenderer.cpp | 30 +- src/datavisualization/engine/scatter3drenderer_p.h | 1 - src/datavisualization/engine/surface3drenderer.cpp | 403 +++-- src/datavisualization/engine/surface3drenderer_p.h | 2 - .../global/datavisualizationglobal_p.h | 1 + src/datavisualization/theme/q3dtheme.cpp | 2 +- src/datavisualization/utils/surfaceobject.cpp | 79 +- src/datavisualization/utils/surfaceobject_p.h | 13 +- src/datavisualizationqml2/abstractdeclarative.cpp | 12 + src/datavisualizationqml2/abstractdeclarative_p.h | 5 + .../datavisualizationqml2_plugin.cpp | 6 + tests/surfacetest/graphmodifier.cpp | 5 + tests/surfacetest/graphmodifier.h | 1 + tests/surfacetest/main.cpp | 7 + 31 files changed, 2575 insertions(+), 240 deletions(-) create mode 100644 examples/datavisualization/qmlspectrogram/doc/src/qmlspectrogram.qdoc create mode 100644 examples/datavisualization/qmlspectrogram/main.cpp create mode 100644 examples/datavisualization/qmlspectrogram/qml/qmlspectrogram/Data.qml create mode 100644 examples/datavisualization/qmlspectrogram/qml/qmlspectrogram/NewButton.qml create mode 100644 examples/datavisualization/qmlspectrogram/qml/qmlspectrogram/main.qml create mode 100644 examples/datavisualization/qmlspectrogram/qmlspectrogram.pro create mode 100644 examples/datavisualization/qmlspectrogram/qmlspectrogram.qrc diff --git a/examples/datavisualization/datavisualization.pro b/examples/datavisualization/datavisualization.pro index 1f72820c..c078630d 100644 --- a/examples/datavisualization/datavisualization.pro +++ b/examples/datavisualization/datavisualization.pro @@ -8,7 +8,8 @@ SUBDIRS += qmlbars \ qmloscilloscope \ qmlsurfacelayers \ qmlaxisformatter \ - qmlaxisdrag + qmlaxisdrag \ + qmlspectrogram !android:!ios { SUBDIRS += bars \ diff --git a/examples/datavisualization/qmlspectrogram/doc/src/qmlspectrogram.qdoc b/examples/datavisualization/qmlspectrogram/doc/src/qmlspectrogram.qdoc new file mode 100644 index 00000000..6ebba93b --- /dev/null +++ b/examples/datavisualization/qmlspectrogram/doc/src/qmlspectrogram.qdoc @@ -0,0 +1,41 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +/*! + \example qmlspectrogram + \title Qt Quick 2 Spectrogram Example + \ingroup qtdatavisualization_examples + \brief Showing spectrogram graph in a QML application. + + The Qt Quick 2 surface example demonstrates how to show a polar and cartesian spectrograms + and how to utilize orthographic projection to show them in 2D. + + TODO!!! + + \image qmlspectrogram-example.png + + The focus in this example is on showing how to display spectrograms, so the basic + functionality is not explained. For more detailed QML example documentation, + see \l{Qt Quick 2 Scatter Example}. + + \section1 TODO + + \snippet qmlspectrogram/qml/qmlspectrogram/main.qml 0 + + \section1 Example contents +*/ diff --git a/examples/datavisualization/qmlspectrogram/main.cpp b/examples/datavisualization/qmlspectrogram/main.cpp new file mode 100644 index 00000000..87665564 --- /dev/null +++ b/examples/datavisualization/qmlspectrogram/main.cpp @@ -0,0 +1,42 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +#include +#include +#include +#include + +int main(int argc, char *argv[]) +{ + QGuiApplication app(argc, argv); + + QQmlApplicationEngine engine; + + // The following are needed to make examples run without having to install the module + // in desktop environments. +#ifdef Q_OS_WIN + QString extraImportPath(QStringLiteral("%1/../../../../%2")); +#else + QString extraImportPath(QStringLiteral("%1/../../../%2")); +#endif + engine.addImportPath(extraImportPath.arg(QGuiApplication::applicationDirPath(), + QString::fromLatin1("qml"))); + engine.load(QUrl(QStringLiteral("qrc:/qml/qml/qmlspectrogram/main.qml"))); + + return app.exec(); +} diff --git a/examples/datavisualization/qmlspectrogram/qml/qmlspectrogram/Data.qml b/examples/datavisualization/qmlspectrogram/qml/qmlspectrogram/Data.qml new file mode 100644 index 00000000..fc54edf4 --- /dev/null +++ b/examples/datavisualization/qmlspectrogram/qml/qmlspectrogram/Data.qml @@ -0,0 +1,1560 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +import QtQuick 2.1 + +Item { + property alias model: dataModel + + ListModel { + id: dataModel + ListElement{ radius: "0"; angle: "0"; value: "50"; } + ListElement{ radius: "0"; angle: "5"; value: "54.3578"; } + ListElement{ radius: "0"; angle: "10"; value: "58.6824"; } + ListElement{ radius: "0"; angle: "15"; value: "62.941"; } + ListElement{ radius: "0"; angle: "20"; value: "67.101"; } + ListElement{ radius: "0"; angle: "25"; value: "71.1309"; } + ListElement{ radius: "0"; angle: "30"; value: "75"; } + ListElement{ radius: "0"; angle: "35"; value: "78.6788"; } + ListElement{ radius: "0"; angle: "40"; value: "82.1394"; } + ListElement{ radius: "0"; angle: "45"; value: "85.3553"; } + ListElement{ radius: "0"; angle: "50"; value: "88.3022"; } + ListElement{ radius: "0"; angle: "55"; value: "90.9576"; } + ListElement{ radius: "0"; angle: "60"; value: "93.3013"; } + ListElement{ radius: "0"; angle: "65"; value: "95.3154"; } + ListElement{ radius: "0"; angle: "70"; value: "96.9846"; } + ListElement{ radius: "0"; angle: "75"; value: "98.2963"; } + ListElement{ radius: "0"; angle: "80"; value: "99.2404"; } + ListElement{ radius: "0"; angle: "85"; value: "99.8097"; } + ListElement{ radius: "0"; angle: "90"; value: "100"; } + ListElement{ radius: "0"; angle: "95"; value: "99.8097"; } + ListElement{ radius: "0"; angle: "100"; value: "99.2404"; } + ListElement{ radius: "0"; angle: "105"; value: "98.2963"; } + ListElement{ radius: "0"; angle: "110"; value: "96.9846"; } + ListElement{ radius: "0"; angle: "115"; value: "95.3154"; } + ListElement{ radius: "0"; angle: "120"; value: "93.3013"; } + ListElement{ radius: "0"; angle: "125"; value: "90.9576"; } + ListElement{ radius: "0"; angle: "130"; value: "88.3022"; } + ListElement{ radius: "0"; angle: "135"; value: "85.3553"; } + ListElement{ radius: "0"; angle: "140"; value: "82.1394"; } + ListElement{ radius: "0"; angle: "145"; value: "78.6788"; } + ListElement{ radius: "0"; angle: "150"; value: "75"; } + ListElement{ radius: "0"; angle: "155"; value: "71.1309"; } + ListElement{ radius: "0"; angle: "160"; value: "67.101"; } + ListElement{ radius: "0"; angle: "165"; value: "62.941"; } + ListElement{ radius: "0"; angle: "170"; value: "58.6824"; } + ListElement{ radius: "0"; angle: "175"; value: "54.3578"; } + ListElement{ radius: "0"; angle: "180"; value: "50"; } + ListElement{ radius: "0"; angle: "185"; value: "45.6422"; } + ListElement{ radius: "0"; angle: "190"; value: "41.3176"; } + ListElement{ radius: "0"; angle: "195"; value: "37.059"; } + ListElement{ radius: "0"; angle: "200"; value: "32.899"; } + ListElement{ radius: "0"; angle: "205"; value: "28.8691"; } + ListElement{ radius: "0"; angle: "210"; value: "25"; } + ListElement{ radius: "0"; angle: "215"; value: "21.3212"; } + ListElement{ radius: "0"; angle: "220"; value: "17.8606"; } + ListElement{ radius: "0"; angle: "225"; value: "14.6447"; } + ListElement{ radius: "0"; angle: "230"; value: "11.6978"; } + ListElement{ radius: "0"; angle: "235"; value: "9.0424"; } + ListElement{ radius: "0"; angle: "240"; value: "6.69873"; } + ListElement{ radius: "0"; angle: "245"; value: "4.68461"; } + ListElement{ radius: "0"; angle: "250"; value: "3.01537"; } + ListElement{ radius: "0"; angle: "255"; value: "1.70371"; } + ListElement{ radius: "0"; angle: "260"; value: "0.759612"; } + ListElement{ radius: "0"; angle: "265"; value: "0.190265"; } + ListElement{ radius: "0"; angle: "270"; value: "0"; } + ListElement{ radius: "0"; angle: "275"; value: "0.190265"; } + ListElement{ radius: "0"; angle: "280"; value: "0.759612"; } + ListElement{ radius: "0"; angle: "285"; value: "1.70371"; } + ListElement{ radius: "0"; angle: "290"; value: "3.01537"; } + ListElement{ radius: "0"; angle: "295"; value: "4.68461"; } + ListElement{ radius: "0"; angle: "300"; value: "6.69873"; } + ListElement{ radius: "0"; angle: "305"; value: "9.0424"; } + ListElement{ radius: "0"; angle: "310"; value: "11.6978"; } + ListElement{ radius: "0"; angle: "315"; value: "14.6447"; } + ListElement{ radius: "0"; angle: "320"; value: "17.8606"; } + ListElement{ radius: "0"; angle: "325"; value: "21.3212"; } + ListElement{ radius: "0"; angle: "330"; value: "25"; } + ListElement{ radius: "0"; angle: "335"; value: "28.8691"; } + ListElement{ radius: "0"; angle: "340"; value: "32.899"; } + ListElement{ radius: "0"; angle: "345"; value: "37.059"; } + ListElement{ radius: "0"; angle: "350"; value: "41.3176"; } + ListElement{ radius: "0"; angle: "355"; value: "45.6422"; } + ListElement{ radius: "0"; angle: "360"; value: "50"; } + ListElement{ radius: "5"; angle: "0"; value: "49.3844"; } + ListElement{ radius: "5"; angle: "5"; value: "53.7422"; } + ListElement{ radius: "5"; angle: "10"; value: "58.0668"; } + ListElement{ radius: "5"; angle: "15"; value: "62.3254"; } + ListElement{ radius: "5"; angle: "20"; value: "66.4854"; } + ListElement{ radius: "5"; angle: "25"; value: "70.5153"; } + ListElement{ radius: "5"; angle: "30"; value: "74.3844"; } + ListElement{ radius: "5"; angle: "35"; value: "78.0632"; } + ListElement{ radius: "5"; angle: "40"; value: "81.5238"; } + ListElement{ radius: "5"; angle: "45"; value: "84.7398"; } + ListElement{ radius: "5"; angle: "50"; value: "87.6866"; } + ListElement{ radius: "5"; angle: "55"; value: "90.342"; } + ListElement{ radius: "5"; angle: "60"; value: "92.6857"; } + ListElement{ radius: "5"; angle: "65"; value: "94.6998"; } + ListElement{ radius: "5"; angle: "70"; value: "96.369"; } + ListElement{ radius: "5"; angle: "75"; value: "97.6807"; } + ListElement{ radius: "5"; angle: "80"; value: "98.6248"; } + ListElement{ radius: "5"; angle: "85"; value: "99.1942"; } + ListElement{ radius: "5"; angle: "90"; value: "99.3844"; } + ListElement{ radius: "5"; angle: "95"; value: "99.1942"; } + ListElement{ radius: "5"; angle: "100"; value: "98.6248"; } + ListElement{ radius: "5"; angle: "105"; value: "97.6807"; } + ListElement{ radius: "5"; angle: "110"; value: "96.369"; } + ListElement{ radius: "5"; angle: "115"; value: "94.6998"; } + ListElement{ radius: "5"; angle: "120"; value: "92.6857"; } + ListElement{ radius: "5"; angle: "125"; value: "90.342"; } + ListElement{ radius: "5"; angle: "130"; value: "87.6866"; } + ListElement{ radius: "5"; angle: "135"; value: "84.7398"; } + ListElement{ radius: "5"; angle: "140"; value: "81.5238"; } + ListElement{ radius: "5"; angle: "145"; value: "78.0632"; } + ListElement{ radius: "5"; angle: "150"; value: "74.3844"; } + ListElement{ radius: "5"; angle: "155"; value: "70.5153"; } + ListElement{ radius: "5"; angle: "160"; value: "66.4854"; } + ListElement{ radius: "5"; angle: "165"; value: "62.3254"; } + ListElement{ radius: "5"; angle: "170"; value: "58.0668"; } + ListElement{ radius: "5"; angle: "175"; value: "53.7422"; } + ListElement{ radius: "5"; angle: "180"; value: "49.3844"; } + ListElement{ radius: "5"; angle: "185"; value: "45.0266"; } + ListElement{ radius: "5"; angle: "190"; value: "40.702"; } + ListElement{ radius: "5"; angle: "195"; value: "36.4435"; } + ListElement{ radius: "5"; angle: "200"; value: "32.2834"; } + ListElement{ radius: "5"; angle: "205"; value: "28.2535"; } + ListElement{ radius: "5"; angle: "210"; value: "24.3844"; } + ListElement{ radius: "5"; angle: "215"; value: "20.7056"; } + ListElement{ radius: "5"; angle: "220"; value: "17.245"; } + ListElement{ radius: "5"; angle: "225"; value: "14.0291"; } + ListElement{ radius: "5"; angle: "230"; value: "11.0822"; } + ListElement{ radius: "5"; angle: "235"; value: "8.42681"; } + ListElement{ radius: "5"; angle: "240"; value: "6.08315"; } + ListElement{ radius: "5"; angle: "245"; value: "4.06903"; } + ListElement{ radius: "5"; angle: "250"; value: "2.39979"; } + ListElement{ radius: "5"; angle: "255"; value: "1.08813"; } + ListElement{ radius: "5"; angle: "260"; value: "0.144029"; } + ListElement{ radius: "5"; angle: "265"; value: "-0.425318"; } + ListElement{ radius: "5"; angle: "270"; value: "-0.615583"; } + ListElement{ radius: "5"; angle: "275"; value: "-0.425318"; } + ListElement{ radius: "5"; angle: "280"; value: "0.144029"; } + ListElement{ radius: "5"; angle: "285"; value: "1.08813"; } + ListElement{ radius: "5"; angle: "290"; value: "2.39979"; } + ListElement{ radius: "5"; angle: "295"; value: "4.06903"; } + ListElement{ radius: "5"; angle: "300"; value: "6.08315"; } + ListElement{ radius: "5"; angle: "305"; value: "8.42681"; } + ListElement{ radius: "5"; angle: "310"; value: "11.0822"; } + ListElement{ radius: "5"; angle: "315"; value: "14.0291"; } + ListElement{ radius: "5"; angle: "320"; value: "17.245"; } + ListElement{ radius: "5"; angle: "325"; value: "20.7056"; } + ListElement{ radius: "5"; angle: "330"; value: "24.3844"; } + ListElement{ radius: "5"; angle: "335"; value: "28.2535"; } + ListElement{ radius: "5"; angle: "340"; value: "32.2834"; } + ListElement{ radius: "5"; angle: "345"; value: "36.4435"; } + ListElement{ radius: "5"; angle: "350"; value: "40.702"; } + ListElement{ radius: "5"; angle: "355"; value: "45.0266"; } + ListElement{ radius: "5"; angle: "360"; value: "49.3844"; } + ListElement{ radius: "10"; angle: "0"; value: "47.5528"; } + ListElement{ radius: "10"; angle: "5"; value: "51.9106"; } + ListElement{ radius: "10"; angle: "10"; value: "56.2352"; } + ListElement{ radius: "10"; angle: "15"; value: "60.4938"; } + ListElement{ radius: "10"; angle: "20"; value: "64.6538"; } + ListElement{ radius: "10"; angle: "25"; value: "68.6837"; } + ListElement{ radius: "10"; angle: "30"; value: "72.5528"; } + ListElement{ radius: "10"; angle: "35"; value: "76.2316"; } + ListElement{ radius: "10"; angle: "40"; value: "79.6922"; } + ListElement{ radius: "10"; angle: "45"; value: "82.9082"; } + ListElement{ radius: "10"; angle: "50"; value: "85.855"; } + ListElement{ radius: "10"; angle: "55"; value: "88.5104"; } + ListElement{ radius: "10"; angle: "60"; value: "90.8541"; } + ListElement{ radius: "10"; angle: "65"; value: "92.8682"; } + ListElement{ radius: "10"; angle: "70"; value: "94.5375"; } + ListElement{ radius: "10"; angle: "75"; value: "95.8491"; } + ListElement{ radius: "10"; angle: "80"; value: "96.7932"; } + ListElement{ radius: "10"; angle: "85"; value: "97.3626"; } + ListElement{ radius: "10"; angle: "90"; value: "97.5528"; } + ListElement{ radius: "10"; angle: "95"; value: "97.3626"; } + ListElement{ radius: "10"; angle: "100"; value: "96.7932"; } + ListElement{ radius: "10"; angle: "105"; value: "95.8491"; } + ListElement{ radius: "10"; angle: "110"; value: "94.5375"; } + ListElement{ radius: "10"; angle: "115"; value: "92.8682"; } + ListElement{ radius: "10"; angle: "120"; value: "90.8541"; } + ListElement{ radius: "10"; angle: "125"; value: "88.5104"; } + ListElement{ radius: "10"; angle: "130"; value: "85.855"; } + ListElement{ radius: "10"; angle: "135"; value: "82.9082"; } + ListElement{ radius: "10"; angle: "140"; value: "79.6922"; } + ListElement{ radius: "10"; angle: "145"; value: "76.2316"; } + ListElement{ radius: "10"; angle: "150"; value: "72.5528"; } + ListElement{ radius: "10"; angle: "155"; value: "68.6837"; } + ListElement{ radius: "10"; angle: "160"; value: "64.6538"; } + ListElement{ radius: "10"; angle: "165"; value: "60.4938"; } + ListElement{ radius: "10"; angle: "170"; value: "56.2352"; } + ListElement{ radius: "10"; angle: "175"; value: "51.9106"; } + ListElement{ radius: "10"; angle: "180"; value: "47.5528"; } + ListElement{ radius: "10"; angle: "185"; value: "43.195"; } + ListElement{ radius: "10"; angle: "190"; value: "38.8704"; } + ListElement{ radius: "10"; angle: "195"; value: "34.6119"; } + ListElement{ radius: "10"; angle: "200"; value: "30.4518"; } + ListElement{ radius: "10"; angle: "205"; value: "26.4219"; } + ListElement{ radius: "10"; angle: "210"; value: "22.5528"; } + ListElement{ radius: "10"; angle: "215"; value: "18.874"; } + ListElement{ radius: "10"; angle: "220"; value: "15.4134"; } + ListElement{ radius: "10"; angle: "225"; value: "12.1975"; } + ListElement{ radius: "10"; angle: "230"; value: "9.2506"; } + ListElement{ radius: "10"; angle: "235"; value: "6.59522"; } + ListElement{ radius: "10"; angle: "240"; value: "4.25156"; } + ListElement{ radius: "10"; angle: "245"; value: "2.23744"; } + ListElement{ radius: "10"; angle: "250"; value: "0.568195"; } + ListElement{ radius: "10"; angle: "255"; value: "-0.743465"; } + ListElement{ radius: "10"; angle: "260"; value: "-1.68756"; } + ListElement{ radius: "10"; angle: "265"; value: "-2.25691"; } + ListElement{ radius: "10"; angle: "270"; value: "-2.44717"; } + ListElement{ radius: "10"; angle: "275"; value: "-2.25691"; } + ListElement{ radius: "10"; angle: "280"; value: "-1.68756"; } + ListElement{ radius: "10"; angle: "285"; value: "-0.743465"; } + ListElement{ radius: "10"; angle: "290"; value: "0.568195"; } + ListElement{ radius: "10"; angle: "295"; value: "2.23744"; } + ListElement{ radius: "10"; angle: "300"; value: "4.25156"; } + ListElement{ radius: "10"; angle: "305"; value: "6.59522"; } + ListElement{ radius: "10"; angle: "310"; value: "9.2506"; } + ListElement{ radius: "10"; angle: "315"; value: "12.1975"; } + ListElement{ radius: "10"; angle: "320"; value: "15.4134"; } + ListElement{ radius: "10"; angle: "325"; value: "18.874"; } + ListElement{ radius: "10"; angle: "330"; value: "22.5528"; } + ListElement{ radius: "10"; angle: "335"; value: "26.4219"; } + ListElement{ radius: "10"; angle: "340"; value: "30.4518"; } + ListElement{ radius: "10"; angle: "345"; value: "34.6119"; } + ListElement{ radius: "10"; angle: "350"; value: "38.8704"; } + ListElement{ radius: "10"; angle: "355"; value: "43.195"; } + ListElement{ radius: "10"; angle: "360"; value: "47.5528"; } + ListElement{ radius: "15"; angle: "0"; value: "44.5503"; } + ListElement{ radius: "15"; angle: "5"; value: "48.9081"; } + ListElement{ radius: "15"; angle: "10"; value: "53.2327"; } + ListElement{ radius: "15"; angle: "15"; value: "57.4913"; } + ListElement{ radius: "15"; angle: "20"; value: "61.6513"; } + ListElement{ radius: "15"; angle: "25"; value: "65.6812"; } + ListElement{ radius: "15"; angle: "30"; value: "69.5503"; } + ListElement{ radius: "15"; angle: "35"; value: "73.2291"; } + ListElement{ radius: "15"; angle: "40"; value: "76.6897"; } + ListElement{ radius: "15"; angle: "45"; value: "79.9057"; } + ListElement{ radius: "15"; angle: "50"; value: "82.8525"; } + ListElement{ radius: "15"; angle: "55"; value: "85.5079"; } + ListElement{ radius: "15"; angle: "60"; value: "87.8516"; } + ListElement{ radius: "15"; angle: "65"; value: "89.8657"; } + ListElement{ radius: "15"; angle: "70"; value: "91.535"; } + ListElement{ radius: "15"; angle: "75"; value: "92.8466"; } + ListElement{ radius: "15"; angle: "80"; value: "93.7907"; } + ListElement{ radius: "15"; angle: "85"; value: "94.3601"; } + ListElement{ radius: "15"; angle: "90"; value: "94.5503"; } + ListElement{ radius: "15"; angle: "95"; value: "94.3601"; } + ListElement{ radius: "15"; angle: "100"; value: "93.7907"; } + ListElement{ radius: "15"; angle: "105"; value: "92.8466"; } + ListElement{ radius: "15"; angle: "110"; value: "91.535"; } + ListElement{ radius: "15"; angle: "115"; value: "89.8657"; } + ListElement{ radius: "15"; angle: "120"; value: "87.8516"; } + ListElement{ radius: "15"; angle: "125"; value: "85.5079"; } + ListElement{ radius: "15"; angle: "130"; value: "82.8525"; } + ListElement{ radius: "15"; angle: "135"; value: "79.9057"; } + ListElement{ radius: "15"; angle: "140"; value: "76.6897"; } + ListElement{ radius: "15"; angle: "145"; value: "73.2291"; } + ListElement{ radius: "15"; angle: "150"; value: "69.5503"; } + ListElement{ radius: "15"; angle: "155"; value: "65.6812"; } + ListElement{ radius: "15"; angle: "160"; value: "61.6513"; } + ListElement{ radius: "15"; angle: "165"; value: "57.4913"; } + ListElement{ radius: "15"; angle: "170"; value: "53.2327"; } + ListElement{ radius: "15"; angle: "175"; value: "48.9081"; } + ListElement{ radius: "15"; angle: "180"; value: "44.5503"; } + ListElement{ radius: "15"; angle: "185"; value: "40.1925"; } + ListElement{ radius: "15"; angle: "190"; value: "35.8679"; } + ListElement{ radius: "15"; angle: "195"; value: "31.6094"; } + ListElement{ radius: "15"; angle: "200"; value: "27.4493"; } + ListElement{ radius: "15"; angle: "205"; value: "23.4194"; } + ListElement{ radius: "15"; angle: "210"; value: "19.5503"; } + ListElement{ radius: "15"; angle: "215"; value: "15.8715"; } + ListElement{ radius: "15"; angle: "220"; value: "12.4109"; } + ListElement{ radius: "15"; angle: "225"; value: "9.19499"; } + ListElement{ radius: "15"; angle: "230"; value: "6.2481"; } + ListElement{ radius: "15"; angle: "235"; value: "3.59272"; } + ListElement{ radius: "15"; angle: "240"; value: "1.24906"; } + ListElement{ radius: "15"; angle: "245"; value: "-0.765063"; } + ListElement{ radius: "15"; angle: "250"; value: "-2.4343"; } + ListElement{ radius: "15"; angle: "255"; value: "-3.74597"; } + ListElement{ radius: "15"; angle: "260"; value: "-4.69006"; } + ListElement{ radius: "15"; angle: "265"; value: "-5.25941"; } + ListElement{ radius: "15"; angle: "270"; value: "-5.44967"; } + ListElement{ radius: "15"; angle: "275"; value: "-5.25941"; } + ListElement{ radius: "15"; angle: "280"; value: "-4.69006"; } + ListElement{ radius: "15"; angle: "285"; value: "-3.74597"; } + ListElement{ radius: "15"; angle: "290"; value: "-2.4343"; } + ListElement{ radius: "15"; angle: "295"; value: "-0.765063"; } + ListElement{ radius: "15"; angle: "300"; value: "1.24906"; } + ListElement{ radius: "15"; angle: "305"; value: "3.59272"; } + ListElement{ radius: "15"; angle: "310"; value: "6.2481"; } + ListElement{ radius: "15"; angle: "315"; value: "9.19499"; } + ListElement{ radius: "15"; angle: "320"; value: "12.4109"; } + ListElement{ radius: "15"; angle: "325"; value: "15.8715"; } + ListElement{ radius: "15"; angle: "330"; value: "19.5503"; } + ListElement{ radius: "15"; angle: "335"; value: "23.4194"; } + ListElement{ radius: "15"; angle: "340"; value: "27.4493"; } + ListElement{ radius: "15"; angle: "345"; value: "31.6094"; } + ListElement{ radius: "15"; angle: "350"; value: "35.8679"; } + ListElement{ radius: "15"; angle: "355"; value: "40.1925"; } + ListElement{ radius: "15"; angle: "360"; value: "44.5503"; } + ListElement{ radius: "20"; angle: "0"; value: "40.4508"; } + ListElement{ radius: "20"; angle: "5"; value: "44.8086"; } + ListElement{ radius: "20"; angle: "10"; value: "49.1333"; } + ListElement{ radius: "20"; angle: "15"; value: "53.3918"; } + ListElement{ radius: "20"; angle: "20"; value: "57.5519"; } + ListElement{ radius: "20"; angle: "25"; value: "61.5818"; } + ListElement{ radius: "20"; angle: "30"; value: "65.4508"; } + ListElement{ radius: "20"; angle: "35"; value: "69.1297"; } + ListElement{ radius: "20"; angle: "40"; value: "72.5902"; } + ListElement{ radius: "20"; angle: "45"; value: "75.8062"; } + ListElement{ radius: "20"; angle: "50"; value: "78.7531"; } + ListElement{ radius: "20"; angle: "55"; value: "81.4085"; } + ListElement{ radius: "20"; angle: "60"; value: "83.7521"; } + ListElement{ radius: "20"; angle: "65"; value: "85.7662"; } + ListElement{ radius: "20"; angle: "70"; value: "87.4355"; } + ListElement{ radius: "20"; angle: "75"; value: "88.7471"; } + ListElement{ radius: "20"; angle: "80"; value: "89.6912"; } + ListElement{ radius: "20"; angle: "85"; value: "90.2606"; } + ListElement{ radius: "20"; angle: "90"; value: "90.4508"; } + ListElement{ radius: "20"; angle: "95"; value: "90.2606"; } + ListElement{ radius: "20"; angle: "100"; value: "89.6912"; } + ListElement{ radius: "20"; angle: "105"; value: "88.7471"; } + ListElement{ radius: "20"; angle: "110"; value: "87.4355"; } + ListElement{ radius: "20"; angle: "115"; value: "85.7662"; } + ListElement{ radius: "20"; angle: "120"; value: "83.7521"; } + ListElement{ radius: "20"; angle: "125"; value: "81.4085"; } + ListElement{ radius: "20"; angle: "130"; value: "78.7531"; } + ListElement{ radius: "20"; angle: "135"; value: "75.8062"; } + ListElement{ radius: "20"; angle: "140"; value: "72.5902"; } + ListElement{ radius: "20"; angle: "145"; value: "69.1297"; } + ListElement{ radius: "20"; angle: "150"; value: "65.4508"; } + ListElement{ radius: "20"; angle: "155"; value: "61.5818"; } + ListElement{ radius: "20"; angle: "160"; value: "57.5519"; } + ListElement{ radius: "20"; angle: "165"; value: "53.3918"; } + ListElement{ radius: "20"; angle: "170"; value: "49.1333"; } + ListElement{ radius: "20"; angle: "175"; value: "44.8086"; } + ListElement{ radius: "20"; angle: "180"; value: "40.4508"; } + ListElement{ radius: "20"; angle: "185"; value: "36.0931"; } + ListElement{ radius: "20"; angle: "190"; value: "31.7684"; } + ListElement{ radius: "20"; angle: "195"; value: "27.5099"; } + ListElement{ radius: "20"; angle: "200"; value: "23.3498"; } + ListElement{ radius: "20"; angle: "205"; value: "19.3199"; } + ListElement{ radius: "20"; angle: "210"; value: "15.4508"; } + ListElement{ radius: "20"; angle: "215"; value: "11.772"; } + ListElement{ radius: "20"; angle: "220"; value: "8.31147"; } + ListElement{ radius: "20"; angle: "225"; value: "5.09551"; } + ListElement{ radius: "20"; angle: "230"; value: "2.14863"; } + ListElement{ radius: "20"; angle: "235"; value: "-0.506752"; } + ListElement{ radius: "20"; angle: "240"; value: "-2.85042"; } + ListElement{ radius: "20"; angle: "245"; value: "-4.86454"; } + ListElement{ radius: "20"; angle: "250"; value: "-6.53378"; } + ListElement{ radius: "20"; angle: "255"; value: "-7.84544"; } + ListElement{ radius: "20"; angle: "260"; value: "-8.78954"; } + ListElement{ radius: "20"; angle: "265"; value: "-9.35889"; } + ListElement{ radius: "20"; angle: "270"; value: "-9.54915"; } + ListElement{ radius: "20"; angle: "275"; value: "-9.35889"; } + ListElement{ radius: "20"; angle: "280"; value: "-8.78954"; } + ListElement{ radius: "20"; angle: "285"; value: "-7.84544"; } + ListElement{ radius: "20"; angle: "290"; value: "-6.53378"; } + ListElement{ radius: "20"; angle: "295"; value: "-4.86454"; } + ListElement{ radius: "20"; angle: "300"; value: "-2.85042"; } + ListElement{ radius: "20"; angle: "305"; value: "-0.506752"; } + ListElement{ radius: "20"; angle: "310"; value: "2.14863"; } + ListElement{ radius: "20"; angle: "315"; value: "5.09551"; } + ListElement{ radius: "20"; angle: "320"; value: "8.31147"; } + ListElement{ radius: "20"; angle: "325"; value: "11.772"; } + ListElement{ radius: "20"; angle: "330"; value: "15.4508"; } + ListElement{ radius: "20"; angle: "335"; value: "19.3199"; } + ListElement{ radius: "20"; angle: "340"; value: "23.3498"; } + ListElement{ radius: "20"; angle: "345"; value: "27.5099"; } + ListElement{ radius: "20"; angle: "350"; value: "31.7684"; } + ListElement{ radius: "20"; angle: "355"; value: "36.0931"; } + ListElement{ radius: "20"; angle: "360"; value: "40.4508"; } + ListElement{ radius: "25"; angle: "0"; value: "35.3553"; } + ListElement{ radius: "25"; angle: "5"; value: "39.7131"; } + ListElement{ radius: "25"; angle: "10"; value: "44.0377"; } + ListElement{ radius: "25"; angle: "15"; value: "48.2963"; } + ListElement{ radius: "25"; angle: "20"; value: "52.4563"; } + ListElement{ radius: "25"; angle: "25"; value: "56.4863"; } + ListElement{ radius: "25"; angle: "30"; value: "60.3553"; } + ListElement{ radius: "25"; angle: "35"; value: "64.0342"; } + ListElement{ radius: "25"; angle: "40"; value: "67.4947"; } + ListElement{ radius: "25"; angle: "45"; value: "70.7107"; } + ListElement{ radius: "25"; angle: "50"; value: "73.6576"; } + ListElement{ radius: "25"; angle: "55"; value: "76.3129"; } + ListElement{ radius: "25"; angle: "60"; value: "78.6566"; } + ListElement{ radius: "25"; angle: "65"; value: "80.6707"; } + ListElement{ radius: "25"; angle: "70"; value: "82.34"; } + ListElement{ radius: "25"; angle: "75"; value: "83.6516"; } + ListElement{ radius: "25"; angle: "80"; value: "84.5957"; } + ListElement{ radius: "25"; angle: "85"; value: "85.1651"; } + ListElement{ radius: "25"; angle: "90"; value: "85.3553"; } + ListElement{ radius: "25"; angle: "95"; value: "85.1651"; } + ListElement{ radius: "25"; angle: "100"; value: "84.5957"; } + ListElement{ radius: "25"; angle: "105"; value: "83.6516"; } + ListElement{ radius: "25"; angle: "110"; value: "82.34"; } + ListElement{ radius: "25"; angle: "115"; value: "80.6707"; } + ListElement{ radius: "25"; angle: "120"; value: "78.6566"; } + ListElement{ radius: "25"; angle: "125"; value: "76.3129"; } + ListElement{ radius: "25"; angle: "130"; value: "73.6576"; } + ListElement{ radius: "25"; angle: "135"; value: "70.7107"; } + ListElement{ radius: "25"; angle: "140"; value: "67.4947"; } + ListElement{ radius: "25"; angle: "145"; value: "64.0342"; } + ListElement{ radius: "25"; angle: "150"; value: "60.3553"; } + ListElement{ radius: "25"; angle: "155"; value: "56.4863"; } + ListElement{ radius: "25"; angle: "160"; value: "52.4563"; } + ListElement{ radius: "25"; angle: "165"; value: "48.2963"; } + ListElement{ radius: "25"; angle: "170"; value: "44.0377"; } + ListElement{ radius: "25"; angle: "175"; value: "39.7131"; } + ListElement{ radius: "25"; angle: "180"; value: "35.3553"; } + ListElement{ radius: "25"; angle: "185"; value: "30.9976"; } + ListElement{ radius: "25"; angle: "190"; value: "26.6729"; } + ListElement{ radius: "25"; angle: "195"; value: "22.4144"; } + ListElement{ radius: "25"; angle: "200"; value: "18.2543"; } + ListElement{ radius: "25"; angle: "205"; value: "14.2244"; } + ListElement{ radius: "25"; angle: "210"; value: "10.3553"; } + ListElement{ radius: "25"; angle: "215"; value: "6.67652"; } + ListElement{ radius: "25"; angle: "220"; value: "3.21596"; } + ListElement{ radius: "25"; angle: "225"; value: "5.55112e-15"; } + ListElement{ radius: "25"; angle: "230"; value: "-2.94688"; } + ListElement{ radius: "25"; angle: "235"; value: "-5.60226"; } + ListElement{ radius: "25"; angle: "240"; value: "-7.94593"; } + ListElement{ radius: "25"; angle: "245"; value: "-9.96005"; } + ListElement{ radius: "25"; angle: "250"; value: "-11.6293"; } + ListElement{ radius: "25"; angle: "255"; value: "-12.941"; } + ListElement{ radius: "25"; angle: "260"; value: "-13.885"; } + ListElement{ radius: "25"; angle: "265"; value: "-14.4544"; } + ListElement{ radius: "25"; angle: "270"; value: "-14.6447"; } + ListElement{ radius: "25"; angle: "275"; value: "-14.4544"; } + ListElement{ radius: "25"; angle: "280"; value: "-13.885"; } + ListElement{ radius: "25"; angle: "285"; value: "-12.941"; } + ListElement{ radius: "25"; angle: "290"; value: "-11.6293"; } + ListElement{ radius: "25"; angle: "295"; value: "-9.96005"; } + ListElement{ radius: "25"; angle: "300"; value: "-7.94593"; } + ListElement{ radius: "25"; angle: "305"; value: "-5.60226"; } + ListElement{ radius: "25"; angle: "310"; value: "-2.94688"; } + ListElement{ radius: "25"; angle: "315"; value: "-5.55112e-15"; } + ListElement{ radius: "25"; angle: "320"; value: "3.21596"; } + ListElement{ radius: "25"; angle: "325"; value: "6.67652"; } + ListElement{ radius: "25"; angle: "330"; value: "10.3553"; } + ListElement{ radius: "25"; angle: "335"; value: "14.2244"; } + ListElement{ radius: "25"; angle: "340"; value: "18.2543"; } + ListElement{ radius: "25"; angle: "345"; value: "22.4144"; } + ListElement{ radius: "25"; angle: "350"; value: "26.6729"; } + ListElement{ radius: "25"; angle: "355"; value: "30.9976"; } + ListElement{ radius: "25"; angle: "360"; value: "35.3553"; } + ListElement{ radius: "30"; angle: "0"; value: "29.3893"; } + ListElement{ radius: "30"; angle: "5"; value: "33.747"; } + ListElement{ radius: "30"; angle: "10"; value: "38.0717"; } + ListElement{ radius: "30"; angle: "15"; value: "42.3302"; } + ListElement{ radius: "30"; angle: "20"; value: "46.4903"; } + ListElement{ radius: "30"; angle: "25"; value: "50.5202"; } + ListElement{ radius: "30"; angle: "30"; value: "54.3893"; } + ListElement{ radius: "30"; angle: "35"; value: "58.0681"; } + ListElement{ radius: "30"; angle: "40"; value: "61.5286"; } + ListElement{ radius: "30"; angle: "45"; value: "64.7446"; } + ListElement{ radius: "30"; angle: "50"; value: "67.6915"; } + ListElement{ radius: "30"; angle: "55"; value: "70.3469"; } + ListElement{ radius: "30"; angle: "60"; value: "72.6905"; } + ListElement{ radius: "30"; angle: "65"; value: "74.7047"; } + ListElement{ radius: "30"; angle: "70"; value: "76.3739"; } + ListElement{ radius: "30"; angle: "75"; value: "77.6856"; } + ListElement{ radius: "30"; angle: "80"; value: "78.6297"; } + ListElement{ radius: "30"; angle: "85"; value: "79.199"; } + ListElement{ radius: "30"; angle: "90"; value: "79.3893"; } + ListElement{ radius: "30"; angle: "95"; value: "79.199"; } + ListElement{ radius: "30"; angle: "100"; value: "78.6297"; } + ListElement{ radius: "30"; angle: "105"; value: "77.6856"; } + ListElement{ radius: "30"; angle: "110"; value: "76.3739"; } + ListElement{ radius: "30"; angle: "115"; value: "74.7047"; } + ListElement{ radius: "30"; angle: "120"; value: "72.6905"; } + ListElement{ radius: "30"; angle: "125"; value: "70.3469"; } + ListElement{ radius: "30"; angle: "130"; value: "67.6915"; } + ListElement{ radius: "30"; angle: "135"; value: "64.7446"; } + ListElement{ radius: "30"; angle: "140"; value: "61.5286"; } + ListElement{ radius: "30"; angle: "145"; value: "58.0681"; } + ListElement{ radius: "30"; angle: "150"; value: "54.3893"; } + ListElement{ radius: "30"; angle: "155"; value: "50.5202"; } + ListElement{ radius: "30"; angle: "160"; value: "46.4903"; } + ListElement{ radius: "30"; angle: "165"; value: "42.3302"; } + ListElement{ radius: "30"; angle: "170"; value: "38.0717"; } + ListElement{ radius: "30"; angle: "175"; value: "33.747"; } + ListElement{ radius: "30"; angle: "180"; value: "29.3893"; } + ListElement{ radius: "30"; angle: "185"; value: "25.0315"; } + ListElement{ radius: "30"; angle: "190"; value: "20.7069"; } + ListElement{ radius: "30"; angle: "195"; value: "16.4483"; } + ListElement{ radius: "30"; angle: "200"; value: "12.2883"; } + ListElement{ radius: "30"; angle: "205"; value: "8.25835"; } + ListElement{ radius: "30"; angle: "210"; value: "4.38926"; } + ListElement{ radius: "30"; angle: "215"; value: "0.710441"; } + ListElement{ radius: "30"; angle: "220"; value: "-2.75012"; } + ListElement{ radius: "30"; angle: "225"; value: "-5.96608"; } + ListElement{ radius: "30"; angle: "230"; value: "-8.91296"; } + ListElement{ radius: "30"; angle: "235"; value: "-11.5683"; } + ListElement{ radius: "30"; angle: "240"; value: "-13.912"; } + ListElement{ radius: "30"; angle: "245"; value: "-15.9261"; } + ListElement{ radius: "30"; angle: "250"; value: "-17.5954"; } + ListElement{ radius: "30"; angle: "255"; value: "-18.907"; } + ListElement{ radius: "30"; angle: "260"; value: "-19.8511"; } + ListElement{ radius: "30"; angle: "265"; value: "-20.4205"; } + ListElement{ radius: "30"; angle: "270"; value: "-20.6107"; } + ListElement{ radius: "30"; angle: "275"; value: "-20.4205"; } + ListElement{ radius: "30"; angle: "280"; value: "-19.8511"; } + ListElement{ radius: "30"; angle: "285"; value: "-18.907"; } + ListElement{ radius: "30"; angle: "290"; value: "-17.5954"; } + ListElement{ radius: "30"; angle: "295"; value: "-15.9261"; } + ListElement{ radius: "30"; angle: "300"; value: "-13.912"; } + ListElement{ radius: "30"; angle: "305"; value: "-11.5683"; } + ListElement{ radius: "30"; angle: "310"; value: "-8.91296"; } + ListElement{ radius: "30"; angle: "315"; value: "-5.96608"; } + ListElement{ radius: "30"; angle: "320"; value: "-2.75012"; } + ListElement{ radius: "30"; angle: "325"; value: "0.710441"; } + ListElement{ radius: "30"; angle: "330"; value: "4.38926"; } + ListElement{ radius: "30"; angle: "335"; value: "8.25835"; } + ListElement{ radius: "30"; angle: "340"; value: "12.2883"; } + ListElement{ radius: "30"; angle: "345"; value: "16.4483"; } + ListElement{ radius: "30"; angle: "350"; value: "20.7069"; } + ListElement{ radius: "30"; angle: "355"; value: "25.0315"; } + ListElement{ radius: "30"; angle: "360"; value: "29.3893"; } + ListElement{ radius: "35"; angle: "0"; value: "22.6995"; } + ListElement{ radius: "35"; angle: "5"; value: "27.0573"; } + ListElement{ radius: "35"; angle: "10"; value: "31.3819"; } + ListElement{ radius: "35"; angle: "15"; value: "35.6405"; } + ListElement{ radius: "35"; angle: "20"; value: "39.8005"; } + ListElement{ radius: "35"; angle: "25"; value: "43.8304"; } + ListElement{ radius: "35"; angle: "30"; value: "47.6995"; } + ListElement{ radius: "35"; angle: "35"; value: "51.3783"; } + ListElement{ radius: "35"; angle: "40"; value: "54.8389"; } + ListElement{ radius: "35"; angle: "45"; value: "58.0549"; } + ListElement{ radius: "35"; angle: "50"; value: "61.0017"; } + ListElement{ radius: "35"; angle: "55"; value: "63.6571"; } + ListElement{ radius: "35"; angle: "60"; value: "66.0008"; } + ListElement{ radius: "35"; angle: "65"; value: "68.0149"; } + ListElement{ radius: "35"; angle: "70"; value: "69.6842"; } + ListElement{ radius: "35"; angle: "75"; value: "70.9958"; } + ListElement{ radius: "35"; angle: "80"; value: "71.9399"; } + ListElement{ radius: "35"; angle: "85"; value: "72.5093"; } + ListElement{ radius: "35"; angle: "90"; value: "72.6995"; } + ListElement{ radius: "35"; angle: "95"; value: "72.5093"; } + ListElement{ radius: "35"; angle: "100"; value: "71.9399"; } + ListElement{ radius: "35"; angle: "105"; value: "70.9958"; } + ListElement{ radius: "35"; angle: "110"; value: "69.6842"; } + ListElement{ radius: "35"; angle: "115"; value: "68.0149"; } + ListElement{ radius: "35"; angle: "120"; value: "66.0008"; } + ListElement{ radius: "35"; angle: "125"; value: "63.6571"; } + ListElement{ radius: "35"; angle: "130"; value: "61.0017"; } + ListElement{ radius: "35"; angle: "135"; value: "58.0549"; } + ListElement{ radius: "35"; angle: "140"; value: "54.8389"; } + ListElement{ radius: "35"; angle: "145"; value: "51.3783"; } + ListElement{ radius: "35"; angle: "150"; value: "47.6995"; } + ListElement{ radius: "35"; angle: "155"; value: "43.8304"; } + ListElement{ radius: "35"; angle: "160"; value: "39.8005"; } + ListElement{ radius: "35"; angle: "165"; value: "35.6405"; } + ListElement{ radius: "35"; angle: "170"; value: "31.3819"; } + ListElement{ radius: "35"; angle: "175"; value: "27.0573"; } + ListElement{ radius: "35"; angle: "180"; value: "22.6995"; } + ListElement{ radius: "35"; angle: "185"; value: "18.3417"; } + ListElement{ radius: "35"; angle: "190"; value: "14.0171"; } + ListElement{ radius: "35"; angle: "195"; value: "9.75857"; } + ListElement{ radius: "35"; angle: "200"; value: "5.59852"; } + ListElement{ radius: "35"; angle: "205"; value: "1.56861"; } + ListElement{ radius: "35"; angle: "210"; value: "-2.30048"; } + ListElement{ radius: "35"; angle: "215"; value: "-5.9793"; } + ListElement{ radius: "35"; angle: "220"; value: "-9.43986"; } + ListElement{ radius: "35"; angle: "225"; value: "-12.6558"; } + ListElement{ radius: "35"; angle: "230"; value: "-15.6027"; } + ListElement{ radius: "35"; angle: "235"; value: "-18.2581"; } + ListElement{ radius: "35"; angle: "240"; value: "-20.6017"; } + ListElement{ radius: "35"; angle: "245"; value: "-22.6159"; } + ListElement{ radius: "35"; angle: "250"; value: "-24.2851"; } + ListElement{ radius: "35"; angle: "255"; value: "-25.5968"; } + ListElement{ radius: "35"; angle: "260"; value: "-26.5409"; } + ListElement{ radius: "35"; angle: "265"; value: "-27.1102"; } + ListElement{ radius: "35"; angle: "270"; value: "-27.3005"; } + ListElement{ radius: "35"; angle: "275"; value: "-27.1102"; } + ListElement{ radius: "35"; angle: "280"; value: "-26.5409"; } + ListElement{ radius: "35"; angle: "285"; value: "-25.5968"; } + ListElement{ radius: "35"; angle: "290"; value: "-24.2851"; } + ListElement{ radius: "35"; angle: "295"; value: "-22.6159"; } + ListElement{ radius: "35"; angle: "300"; value: "-20.6017"; } + ListElement{ radius: "35"; angle: "305"; value: "-18.2581"; } + ListElement{ radius: "35"; angle: "310"; value: "-15.6027"; } + ListElement{ radius: "35"; angle: "315"; value: "-12.6558"; } + ListElement{ radius: "35"; angle: "320"; value: "-9.43986"; } + ListElement{ radius: "35"; angle: "325"; value: "-5.9793"; } + ListElement{ radius: "35"; angle: "330"; value: "-2.30048"; } + ListElement{ radius: "35"; angle: "335"; value: "1.56861"; } + ListElement{ radius: "35"; angle: "340"; value: "5.59852"; } + ListElement{ radius: "35"; angle: "345"; value: "9.75857"; } + ListElement{ radius: "35"; angle: "350"; value: "14.0171"; } + ListElement{ radius: "35"; angle: "355"; value: "18.3417"; } + ListElement{ radius: "35"; angle: "360"; value: "22.6995"; } + ListElement{ radius: "40"; angle: "0"; value: "15.4508"; } + ListElement{ radius: "40"; angle: "5"; value: "19.8086"; } + ListElement{ radius: "40"; angle: "10"; value: "24.1333"; } + ListElement{ radius: "40"; angle: "15"; value: "28.3918"; } + ListElement{ radius: "40"; angle: "20"; value: "32.5519"; } + ListElement{ radius: "40"; angle: "25"; value: "36.5818"; } + ListElement{ radius: "40"; angle: "30"; value: "40.4508"; } + ListElement{ radius: "40"; angle: "35"; value: "44.1297"; } + ListElement{ radius: "40"; angle: "40"; value: "47.5902"; } + ListElement{ radius: "40"; angle: "45"; value: "50.8062"; } + ListElement{ radius: "40"; angle: "50"; value: "53.7531"; } + ListElement{ radius: "40"; angle: "55"; value: "56.4085"; } + ListElement{ radius: "40"; angle: "60"; value: "58.7521"; } + ListElement{ radius: "40"; angle: "65"; value: "60.7662"; } + ListElement{ radius: "40"; angle: "70"; value: "62.4355"; } + ListElement{ radius: "40"; angle: "75"; value: "63.7471"; } + ListElement{ radius: "40"; angle: "80"; value: "64.6912"; } + ListElement{ radius: "40"; angle: "85"; value: "65.2606"; } + ListElement{ radius: "40"; angle: "90"; value: "65.4508"; } + ListElement{ radius: "40"; angle: "95"; value: "65.2606"; } + ListElement{ radius: "40"; angle: "100"; value: "64.6912"; } + ListElement{ radius: "40"; angle: "105"; value: "63.7471"; } + ListElement{ radius: "40"; angle: "110"; value: "62.4355"; } + ListElement{ radius: "40"; angle: "115"; value: "60.7662"; } + ListElement{ radius: "40"; angle: "120"; value: "58.7521"; } + ListElement{ radius: "40"; angle: "125"; value: "56.4085"; } + ListElement{ radius: "40"; angle: "130"; value: "53.7531"; } + ListElement{ radius: "40"; angle: "135"; value: "50.8062"; } + ListElement{ radius: "40"; angle: "140"; value: "47.5902"; } + ListElement{ radius: "40"; angle: "145"; value: "44.1297"; } + ListElement{ radius: "40"; angle: "150"; value: "40.4508"; } + ListElement{ radius: "40"; angle: "155"; value: "36.5818"; } + ListElement{ radius: "40"; angle: "160"; value: "32.5519"; } + ListElement{ radius: "40"; angle: "165"; value: "28.3918"; } + ListElement{ radius: "40"; angle: "170"; value: "24.1333"; } + ListElement{ radius: "40"; angle: "175"; value: "19.8086"; } + ListElement{ radius: "40"; angle: "180"; value: "15.4508"; } + ListElement{ radius: "40"; angle: "185"; value: "11.0931"; } + ListElement{ radius: "40"; angle: "190"; value: "6.76844"; } + ListElement{ radius: "40"; angle: "195"; value: "2.5099"; } + ListElement{ radius: "40"; angle: "200"; value: "-1.65016"; } + ListElement{ radius: "40"; angle: "205"; value: "-5.68006"; } + ListElement{ radius: "40"; angle: "210"; value: "-9.54915"; } + ListElement{ radius: "40"; angle: "215"; value: "-13.228"; } + ListElement{ radius: "40"; angle: "220"; value: "-16.6885"; } + ListElement{ radius: "40"; angle: "225"; value: "-19.9045"; } + ListElement{ radius: "40"; angle: "230"; value: "-22.8514"; } + ListElement{ radius: "40"; angle: "235"; value: "-25.5068"; } + ListElement{ radius: "40"; angle: "240"; value: "-27.8504"; } + ListElement{ radius: "40"; angle: "245"; value: "-29.8645"; } + ListElement{ radius: "40"; angle: "250"; value: "-31.5338"; } + ListElement{ radius: "40"; angle: "255"; value: "-32.8454"; } + ListElement{ radius: "40"; angle: "260"; value: "-33.7895"; } + ListElement{ radius: "40"; angle: "265"; value: "-34.3589"; } + ListElement{ radius: "40"; angle: "270"; value: "-34.5492"; } + ListElement{ radius: "40"; angle: "275"; value: "-34.3589"; } + ListElement{ radius: "40"; angle: "280"; value: "-33.7895"; } + ListElement{ radius: "40"; angle: "285"; value: "-32.8454"; } + ListElement{ radius: "40"; angle: "290"; value: "-31.5338"; } + ListElement{ radius: "40"; angle: "295"; value: "-29.8645"; } + ListElement{ radius: "40"; angle: "300"; value: "-27.8504"; } + ListElement{ radius: "40"; angle: "305"; value: "-25.5068"; } + ListElement{ radius: "40"; angle: "310"; value: "-22.8514"; } + ListElement{ radius: "40"; angle: "315"; value: "-19.9045"; } + ListElement{ radius: "40"; angle: "320"; value: "-16.6885"; } + ListElement{ radius: "40"; angle: "325"; value: "-13.228"; } + ListElement{ radius: "40"; angle: "330"; value: "-9.54915"; } + ListElement{ radius: "40"; angle: "335"; value: "-5.68006"; } + ListElement{ radius: "40"; angle: "340"; value: "-1.65016"; } + ListElement{ radius: "40"; angle: "345"; value: "2.5099"; } + ListElement{ radius: "40"; angle: "350"; value: "6.76844"; } + ListElement{ radius: "40"; angle: "355"; value: "11.0931"; } + ListElement{ radius: "40"; angle: "360"; value: "15.4508"; } + ListElement{ radius: "45"; angle: "0"; value: "7.82172"; } + ListElement{ radius: "45"; angle: "5"; value: "12.1795"; } + ListElement{ radius: "45"; angle: "10"; value: "16.5041"; } + ListElement{ radius: "45"; angle: "15"; value: "20.7627"; } + ListElement{ radius: "45"; angle: "20"; value: "24.9227"; } + ListElement{ radius: "45"; angle: "25"; value: "28.9526"; } + ListElement{ radius: "45"; angle: "30"; value: "32.8217"; } + ListElement{ radius: "45"; angle: "35"; value: "36.5005"; } + ListElement{ radius: "45"; angle: "40"; value: "39.9611"; } + ListElement{ radius: "45"; angle: "45"; value: "43.1771"; } + ListElement{ radius: "45"; angle: "50"; value: "46.1239"; } + ListElement{ radius: "45"; angle: "55"; value: "48.7793"; } + ListElement{ radius: "45"; angle: "60"; value: "51.123"; } + ListElement{ radius: "45"; angle: "65"; value: "53.1371"; } + ListElement{ radius: "45"; angle: "70"; value: "54.8064"; } + ListElement{ radius: "45"; angle: "75"; value: "56.118"; } + ListElement{ radius: "45"; angle: "80"; value: "57.0621"; } + ListElement{ radius: "45"; angle: "85"; value: "57.6315"; } + ListElement{ radius: "45"; angle: "90"; value: "57.8217"; } + ListElement{ radius: "45"; angle: "95"; value: "57.6315"; } + ListElement{ radius: "45"; angle: "100"; value: "57.0621"; } + ListElement{ radius: "45"; angle: "105"; value: "56.118"; } + ListElement{ radius: "45"; angle: "110"; value: "54.8064"; } + ListElement{ radius: "45"; angle: "115"; value: "53.1371"; } + ListElement{ radius: "45"; angle: "120"; value: "51.123"; } + ListElement{ radius: "45"; angle: "125"; value: "48.7793"; } + ListElement{ radius: "45"; angle: "130"; value: "46.1239"; } + ListElement{ radius: "45"; angle: "135"; value: "43.1771"; } + ListElement{ radius: "45"; angle: "140"; value: "39.9611"; } + ListElement{ radius: "45"; angle: "145"; value: "36.5005"; } + ListElement{ radius: "45"; angle: "150"; value: "32.8217"; } + ListElement{ radius: "45"; angle: "155"; value: "28.9526"; } + ListElement{ radius: "45"; angle: "160"; value: "24.9227"; } + ListElement{ radius: "45"; angle: "165"; value: "20.7627"; } + ListElement{ radius: "45"; angle: "170"; value: "16.5041"; } + ListElement{ radius: "45"; angle: "175"; value: "12.1795"; } + ListElement{ radius: "45"; angle: "180"; value: "7.82172"; } + ListElement{ radius: "45"; angle: "185"; value: "3.46394"; } + ListElement{ radius: "45"; angle: "190"; value: "-0.860686"; } + ListElement{ radius: "45"; angle: "195"; value: "-5.11923"; } + ListElement{ radius: "45"; angle: "200"; value: "-9.27928"; } + ListElement{ radius: "45"; angle: "205"; value: "-13.3092"; } + ListElement{ radius: "45"; angle: "210"; value: "-17.1783"; } + ListElement{ radius: "45"; angle: "215"; value: "-20.8571"; } + ListElement{ radius: "45"; angle: "220"; value: "-24.3177"; } + ListElement{ radius: "45"; angle: "225"; value: "-27.5336"; } + ListElement{ radius: "45"; angle: "230"; value: "-30.4805"; } + ListElement{ radius: "45"; angle: "235"; value: "-33.1359"; } + ListElement{ radius: "45"; angle: "240"; value: "-35.4795"; } + ListElement{ radius: "45"; angle: "245"; value: "-37.4937"; } + ListElement{ radius: "45"; angle: "250"; value: "-39.1629"; } + ListElement{ radius: "45"; angle: "255"; value: "-40.4746"; } + ListElement{ radius: "45"; angle: "260"; value: "-41.4187"; } + ListElement{ radius: "45"; angle: "265"; value: "-41.988"; } + ListElement{ radius: "45"; angle: "270"; value: "-42.1783"; } + ListElement{ radius: "45"; angle: "275"; value: "-41.988"; } + ListElement{ radius: "45"; angle: "280"; value: "-41.4187"; } + ListElement{ radius: "45"; angle: "285"; value: "-40.4746"; } + ListElement{ radius: "45"; angle: "290"; value: "-39.1629"; } + ListElement{ radius: "45"; angle: "295"; value: "-37.4937"; } + ListElement{ radius: "45"; angle: "300"; value: "-35.4795"; } + ListElement{ radius: "45"; angle: "305"; value: "-33.1359"; } + ListElement{ radius: "45"; angle: "310"; value: "-30.4805"; } + ListElement{ radius: "45"; angle: "315"; value: "-27.5336"; } + ListElement{ radius: "45"; angle: "320"; value: "-24.3177"; } + ListElement{ radius: "45"; angle: "325"; value: "-20.8571"; } + ListElement{ radius: "45"; angle: "330"; value: "-17.1783"; } + ListElement{ radius: "45"; angle: "335"; value: "-13.3092"; } + ListElement{ radius: "45"; angle: "340"; value: "-9.27928"; } + ListElement{ radius: "45"; angle: "345"; value: "-5.11923"; } + ListElement{ radius: "45"; angle: "350"; value: "-0.860686"; } + ListElement{ radius: "45"; angle: "355"; value: "3.46394"; } + ListElement{ radius: "45"; angle: "360"; value: "7.82172"; } + ListElement{ radius: "50"; angle: "0"; value: "3.06162e-15"; } + ListElement{ radius: "50"; angle: "5"; value: "4.35779"; } + ListElement{ radius: "50"; angle: "10"; value: "8.68241"; } + ListElement{ radius: "50"; angle: "15"; value: "12.941"; } + ListElement{ radius: "50"; angle: "20"; value: "17.101"; } + ListElement{ radius: "50"; angle: "25"; value: "21.1309"; } + ListElement{ radius: "50"; angle: "30"; value: "25"; } + ListElement{ radius: "50"; angle: "35"; value: "28.6788"; } + ListElement{ radius: "50"; angle: "40"; value: "32.1394"; } + ListElement{ radius: "50"; angle: "45"; value: "35.3553"; } + ListElement{ radius: "50"; angle: "50"; value: "38.3022"; } + ListElement{ radius: "50"; angle: "55"; value: "40.9576"; } + ListElement{ radius: "50"; angle: "60"; value: "43.3013"; } + ListElement{ radius: "50"; angle: "65"; value: "45.3154"; } + ListElement{ radius: "50"; angle: "70"; value: "46.9846"; } + ListElement{ radius: "50"; angle: "75"; value: "48.2963"; } + ListElement{ radius: "50"; angle: "80"; value: "49.2404"; } + ListElement{ radius: "50"; angle: "85"; value: "49.8097"; } + ListElement{ radius: "50"; angle: "90"; value: "50"; } + ListElement{ radius: "50"; angle: "95"; value: "49.8097"; } + ListElement{ radius: "50"; angle: "100"; value: "49.2404"; } + ListElement{ radius: "50"; angle: "105"; value: "48.2963"; } + ListElement{ radius: "50"; angle: "110"; value: "46.9846"; } + ListElement{ radius: "50"; angle: "115"; value: "45.3154"; } + ListElement{ radius: "50"; angle: "120"; value: "43.3013"; } + ListElement{ radius: "50"; angle: "125"; value: "40.9576"; } + ListElement{ radius: "50"; angle: "130"; value: "38.3022"; } + ListElement{ radius: "50"; angle: "135"; value: "35.3553"; } + ListElement{ radius: "50"; angle: "140"; value: "32.1394"; } + ListElement{ radius: "50"; angle: "145"; value: "28.6788"; } + ListElement{ radius: "50"; angle: "150"; value: "25"; } + ListElement{ radius: "50"; angle: "155"; value: "21.1309"; } + ListElement{ radius: "50"; angle: "160"; value: "17.101"; } + ListElement{ radius: "50"; angle: "165"; value: "12.941"; } + ListElement{ radius: "50"; angle: "170"; value: "8.68241"; } + ListElement{ radius: "50"; angle: "175"; value: "4.35779"; } + ListElement{ radius: "50"; angle: "180"; value: "9.18485e-15"; } + ListElement{ radius: "50"; angle: "185"; value: "-4.35779"; } + ListElement{ radius: "50"; angle: "190"; value: "-8.68241"; } + ListElement{ radius: "50"; angle: "195"; value: "-12.941"; } + ListElement{ radius: "50"; angle: "200"; value: "-17.101"; } + ListElement{ radius: "50"; angle: "205"; value: "-21.1309"; } + ListElement{ radius: "50"; angle: "210"; value: "-25"; } + ListElement{ radius: "50"; angle: "215"; value: "-28.6788"; } + ListElement{ radius: "50"; angle: "220"; value: "-32.1394"; } + ListElement{ radius: "50"; angle: "225"; value: "-35.3553"; } + ListElement{ radius: "50"; angle: "230"; value: "-38.3022"; } + ListElement{ radius: "50"; angle: "235"; value: "-40.9576"; } + ListElement{ radius: "50"; angle: "240"; value: "-43.3013"; } + ListElement{ radius: "50"; angle: "245"; value: "-45.3154"; } + ListElement{ radius: "50"; angle: "250"; value: "-46.9846"; } + ListElement{ radius: "50"; angle: "255"; value: "-48.2963"; } + ListElement{ radius: "50"; angle: "260"; value: "-49.2404"; } + ListElement{ radius: "50"; angle: "265"; value: "-49.8097"; } + ListElement{ radius: "50"; angle: "270"; value: "-50"; } + ListElement{ radius: "50"; angle: "275"; value: "-49.8097"; } + ListElement{ radius: "50"; angle: "280"; value: "-49.2404"; } + ListElement{ radius: "50"; angle: "285"; value: "-48.2963"; } + ListElement{ radius: "50"; angle: "290"; value: "-46.9846"; } + ListElement{ radius: "50"; angle: "295"; value: "-45.3154"; } + ListElement{ radius: "50"; angle: "300"; value: "-43.3013"; } + ListElement{ radius: "50"; angle: "305"; value: "-40.9576"; } + ListElement{ radius: "50"; angle: "310"; value: "-38.3022"; } + ListElement{ radius: "50"; angle: "315"; value: "-35.3553"; } + ListElement{ radius: "50"; angle: "320"; value: "-32.1394"; } + ListElement{ radius: "50"; angle: "325"; value: "-28.6788"; } + ListElement{ radius: "50"; angle: "330"; value: "-25"; } + ListElement{ radius: "50"; angle: "335"; value: "-21.1309"; } + ListElement{ radius: "50"; angle: "340"; value: "-17.101"; } + ListElement{ radius: "50"; angle: "345"; value: "-12.941"; } + ListElement{ radius: "50"; angle: "350"; value: "-8.68241"; } + ListElement{ radius: "50"; angle: "355"; value: "-4.35779"; } + ListElement{ radius: "50"; angle: "360"; value: "-9.18485e-15"; } + ListElement{ radius: "55"; angle: "0"; value: "-7.82172"; } + ListElement{ radius: "55"; angle: "5"; value: "-3.46394"; } + ListElement{ radius: "55"; angle: "10"; value: "0.860686"; } + ListElement{ radius: "55"; angle: "15"; value: "5.11923"; } + ListElement{ radius: "55"; angle: "20"; value: "9.27928"; } + ListElement{ radius: "55"; angle: "25"; value: "13.3092"; } + ListElement{ radius: "55"; angle: "30"; value: "17.1783"; } + ListElement{ radius: "55"; angle: "35"; value: "20.8571"; } + ListElement{ radius: "55"; angle: "40"; value: "24.3177"; } + ListElement{ radius: "55"; angle: "45"; value: "27.5336"; } + ListElement{ radius: "55"; angle: "50"; value: "30.4805"; } + ListElement{ radius: "55"; angle: "55"; value: "33.1359"; } + ListElement{ radius: "55"; angle: "60"; value: "35.4795"; } + ListElement{ radius: "55"; angle: "65"; value: "37.4937"; } + ListElement{ radius: "55"; angle: "70"; value: "39.1629"; } + ListElement{ radius: "55"; angle: "75"; value: "40.4746"; } + ListElement{ radius: "55"; angle: "80"; value: "41.4187"; } + ListElement{ radius: "55"; angle: "85"; value: "41.988"; } + ListElement{ radius: "55"; angle: "90"; value: "42.1783"; } + ListElement{ radius: "55"; angle: "95"; value: "41.988"; } + ListElement{ radius: "55"; angle: "100"; value: "41.4187"; } + ListElement{ radius: "55"; angle: "105"; value: "40.4746"; } + ListElement{ radius: "55"; angle: "110"; value: "39.1629"; } + ListElement{ radius: "55"; angle: "115"; value: "37.4937"; } + ListElement{ radius: "55"; angle: "120"; value: "35.4795"; } + ListElement{ radius: "55"; angle: "125"; value: "33.1359"; } + ListElement{ radius: "55"; angle: "130"; value: "30.4805"; } + ListElement{ radius: "55"; angle: "135"; value: "27.5336"; } + ListElement{ radius: "55"; angle: "140"; value: "24.3177"; } + ListElement{ radius: "55"; angle: "145"; value: "20.8571"; } + ListElement{ radius: "55"; angle: "150"; value: "17.1783"; } + ListElement{ radius: "55"; angle: "155"; value: "13.3092"; } + ListElement{ radius: "55"; angle: "160"; value: "9.27928"; } + ListElement{ radius: "55"; angle: "165"; value: "5.11923"; } + ListElement{ radius: "55"; angle: "170"; value: "0.860686"; } + ListElement{ radius: "55"; angle: "175"; value: "-3.46394"; } + ListElement{ radius: "55"; angle: "180"; value: "-7.82172"; } + ListElement{ radius: "55"; angle: "185"; value: "-12.1795"; } + ListElement{ radius: "55"; angle: "190"; value: "-16.5041"; } + ListElement{ radius: "55"; angle: "195"; value: "-20.7627"; } + ListElement{ radius: "55"; angle: "200"; value: "-24.9227"; } + ListElement{ radius: "55"; angle: "205"; value: "-28.9526"; } + ListElement{ radius: "55"; angle: "210"; value: "-32.8217"; } + ListElement{ radius: "55"; angle: "215"; value: "-36.5005"; } + ListElement{ radius: "55"; angle: "220"; value: "-39.9611"; } + ListElement{ radius: "55"; angle: "225"; value: "-43.1771"; } + ListElement{ radius: "55"; angle: "230"; value: "-46.1239"; } + ListElement{ radius: "55"; angle: "235"; value: "-48.7793"; } + ListElement{ radius: "55"; angle: "240"; value: "-51.123"; } + ListElement{ radius: "55"; angle: "245"; value: "-53.1371"; } + ListElement{ radius: "55"; angle: "250"; value: "-54.8064"; } + ListElement{ radius: "55"; angle: "255"; value: "-56.118"; } + ListElement{ radius: "55"; angle: "260"; value: "-57.0621"; } + ListElement{ radius: "55"; angle: "265"; value: "-57.6315"; } + ListElement{ radius: "55"; angle: "270"; value: "-57.8217"; } + ListElement{ radius: "55"; angle: "275"; value: "-57.6315"; } + ListElement{ radius: "55"; angle: "280"; value: "-57.0621"; } + ListElement{ radius: "55"; angle: "285"; value: "-56.118"; } + ListElement{ radius: "55"; angle: "290"; value: "-54.8064"; } + ListElement{ radius: "55"; angle: "295"; value: "-53.1371"; } + ListElement{ radius: "55"; angle: "300"; value: "-51.123"; } + ListElement{ radius: "55"; angle: "305"; value: "-48.7793"; } + ListElement{ radius: "55"; angle: "310"; value: "-46.1239"; } + ListElement{ radius: "55"; angle: "315"; value: "-43.1771"; } + ListElement{ radius: "55"; angle: "320"; value: "-39.9611"; } + ListElement{ radius: "55"; angle: "325"; value: "-36.5005"; } + ListElement{ radius: "55"; angle: "330"; value: "-32.8217"; } + ListElement{ radius: "55"; angle: "335"; value: "-28.9526"; } + ListElement{ radius: "55"; angle: "340"; value: "-24.9227"; } + ListElement{ radius: "55"; angle: "345"; value: "-20.7627"; } + ListElement{ radius: "55"; angle: "350"; value: "-16.5041"; } + ListElement{ radius: "55"; angle: "355"; value: "-12.1795"; } + ListElement{ radius: "55"; angle: "360"; value: "-7.82172"; } + ListElement{ radius: "60"; angle: "0"; value: "-15.4508"; } + ListElement{ radius: "60"; angle: "5"; value: "-11.0931"; } + ListElement{ radius: "60"; angle: "10"; value: "-6.76844"; } + ListElement{ radius: "60"; angle: "15"; value: "-2.5099"; } + ListElement{ radius: "60"; angle: "20"; value: "1.65016"; } + ListElement{ radius: "60"; angle: "25"; value: "5.68006"; } + ListElement{ radius: "60"; angle: "30"; value: "9.54915"; } + ListElement{ radius: "60"; angle: "35"; value: "13.228"; } + ListElement{ radius: "60"; angle: "40"; value: "16.6885"; } + ListElement{ radius: "60"; angle: "45"; value: "19.9045"; } + ListElement{ radius: "60"; angle: "50"; value: "22.8514"; } + ListElement{ radius: "60"; angle: "55"; value: "25.5068"; } + ListElement{ radius: "60"; angle: "60"; value: "27.8504"; } + ListElement{ radius: "60"; angle: "65"; value: "29.8645"; } + ListElement{ radius: "60"; angle: "70"; value: "31.5338"; } + ListElement{ radius: "60"; angle: "75"; value: "32.8454"; } + ListElement{ radius: "60"; angle: "80"; value: "33.7895"; } + ListElement{ radius: "60"; angle: "85"; value: "34.3589"; } + ListElement{ radius: "60"; angle: "90"; value: "34.5492"; } + ListElement{ radius: "60"; angle: "95"; value: "34.3589"; } + ListElement{ radius: "60"; angle: "100"; value: "33.7895"; } + ListElement{ radius: "60"; angle: "105"; value: "32.8454"; } + ListElement{ radius: "60"; angle: "110"; value: "31.5338"; } + ListElement{ radius: "60"; angle: "115"; value: "29.8645"; } + ListElement{ radius: "60"; angle: "120"; value: "27.8504"; } + ListElement{ radius: "60"; angle: "125"; value: "25.5068"; } + ListElement{ radius: "60"; angle: "130"; value: "22.8514"; } + ListElement{ radius: "60"; angle: "135"; value: "19.9045"; } + ListElement{ radius: "60"; angle: "140"; value: "16.6885"; } + ListElement{ radius: "60"; angle: "145"; value: "13.228"; } + ListElement{ radius: "60"; angle: "150"; value: "9.54915"; } + ListElement{ radius: "60"; angle: "155"; value: "5.68006"; } + ListElement{ radius: "60"; angle: "160"; value: "1.65016"; } + ListElement{ radius: "60"; angle: "165"; value: "-2.5099"; } + ListElement{ radius: "60"; angle: "170"; value: "-6.76844"; } + ListElement{ radius: "60"; angle: "175"; value: "-11.0931"; } + ListElement{ radius: "60"; angle: "180"; value: "-15.4508"; } + ListElement{ radius: "60"; angle: "185"; value: "-19.8086"; } + ListElement{ radius: "60"; angle: "190"; value: "-24.1333"; } + ListElement{ radius: "60"; angle: "195"; value: "-28.3918"; } + ListElement{ radius: "60"; angle: "200"; value: "-32.5519"; } + ListElement{ radius: "60"; angle: "205"; value: "-36.5818"; } + ListElement{ radius: "60"; angle: "210"; value: "-40.4508"; } + ListElement{ radius: "60"; angle: "215"; value: "-44.1297"; } + ListElement{ radius: "60"; angle: "220"; value: "-47.5902"; } + ListElement{ radius: "60"; angle: "225"; value: "-50.8062"; } + ListElement{ radius: "60"; angle: "230"; value: "-53.7531"; } + ListElement{ radius: "60"; angle: "235"; value: "-56.4085"; } + ListElement{ radius: "60"; angle: "240"; value: "-58.7521"; } + ListElement{ radius: "60"; angle: "245"; value: "-60.7662"; } + ListElement{ radius: "60"; angle: "250"; value: "-62.4355"; } + ListElement{ radius: "60"; angle: "255"; value: "-63.7471"; } + ListElement{ radius: "60"; angle: "260"; value: "-64.6912"; } + ListElement{ radius: "60"; angle: "265"; value: "-65.2606"; } + ListElement{ radius: "60"; angle: "270"; value: "-65.4508"; } + ListElement{ radius: "60"; angle: "275"; value: "-65.2606"; } + ListElement{ radius: "60"; angle: "280"; value: "-64.6912"; } + ListElement{ radius: "60"; angle: "285"; value: "-63.7471"; } + ListElement{ radius: "60"; angle: "290"; value: "-62.4355"; } + ListElement{ radius: "60"; angle: "295"; value: "-60.7662"; } + ListElement{ radius: "60"; angle: "300"; value: "-58.7521"; } + ListElement{ radius: "60"; angle: "305"; value: "-56.4085"; } + ListElement{ radius: "60"; angle: "310"; value: "-53.7531"; } + ListElement{ radius: "60"; angle: "315"; value: "-50.8062"; } + ListElement{ radius: "60"; angle: "320"; value: "-47.5902"; } + ListElement{ radius: "60"; angle: "325"; value: "-44.1297"; } + ListElement{ radius: "60"; angle: "330"; value: "-40.4508"; } + ListElement{ radius: "60"; angle: "335"; value: "-36.5818"; } + ListElement{ radius: "60"; angle: "340"; value: "-32.5519"; } + ListElement{ radius: "60"; angle: "345"; value: "-28.3918"; } + ListElement{ radius: "60"; angle: "350"; value: "-24.1333"; } + ListElement{ radius: "60"; angle: "355"; value: "-19.8086"; } + ListElement{ radius: "60"; angle: "360"; value: "-15.4508"; } + ListElement{ radius: "65"; angle: "0"; value: "-22.6995"; } + ListElement{ radius: "65"; angle: "5"; value: "-18.3417"; } + ListElement{ radius: "65"; angle: "10"; value: "-14.0171"; } + ListElement{ radius: "65"; angle: "15"; value: "-9.75857"; } + ListElement{ radius: "65"; angle: "20"; value: "-5.59852"; } + ListElement{ radius: "65"; angle: "25"; value: "-1.56861"; } + ListElement{ radius: "65"; angle: "30"; value: "2.30048"; } + ListElement{ radius: "65"; angle: "35"; value: "5.9793"; } + ListElement{ radius: "65"; angle: "40"; value: "9.43986"; } + ListElement{ radius: "65"; angle: "45"; value: "12.6558"; } + ListElement{ radius: "65"; angle: "50"; value: "15.6027"; } + ListElement{ radius: "65"; angle: "55"; value: "18.2581"; } + ListElement{ radius: "65"; angle: "60"; value: "20.6017"; } + ListElement{ radius: "65"; angle: "65"; value: "22.6159"; } + ListElement{ radius: "65"; angle: "70"; value: "24.2851"; } + ListElement{ radius: "65"; angle: "75"; value: "25.5968"; } + ListElement{ radius: "65"; angle: "80"; value: "26.5409"; } + ListElement{ radius: "65"; angle: "85"; value: "27.1102"; } + ListElement{ radius: "65"; angle: "90"; value: "27.3005"; } + ListElement{ radius: "65"; angle: "95"; value: "27.1102"; } + ListElement{ radius: "65"; angle: "100"; value: "26.5409"; } + ListElement{ radius: "65"; angle: "105"; value: "25.5968"; } + ListElement{ radius: "65"; angle: "110"; value: "24.2851"; } + ListElement{ radius: "65"; angle: "115"; value: "22.6159"; } + ListElement{ radius: "65"; angle: "120"; value: "20.6017"; } + ListElement{ radius: "65"; angle: "125"; value: "18.2581"; } + ListElement{ radius: "65"; angle: "130"; value: "15.6027"; } + ListElement{ radius: "65"; angle: "135"; value: "12.6558"; } + ListElement{ radius: "65"; angle: "140"; value: "9.43986"; } + ListElement{ radius: "65"; angle: "145"; value: "5.9793"; } + ListElement{ radius: "65"; angle: "150"; value: "2.30048"; } + ListElement{ radius: "65"; angle: "155"; value: "-1.56861"; } + ListElement{ radius: "65"; angle: "160"; value: "-5.59852"; } + ListElement{ radius: "65"; angle: "165"; value: "-9.75857"; } + ListElement{ radius: "65"; angle: "170"; value: "-14.0171"; } + ListElement{ radius: "65"; angle: "175"; value: "-18.3417"; } + ListElement{ radius: "65"; angle: "180"; value: "-22.6995"; } + ListElement{ radius: "65"; angle: "185"; value: "-27.0573"; } + ListElement{ radius: "65"; angle: "190"; value: "-31.3819"; } + ListElement{ radius: "65"; angle: "195"; value: "-35.6405"; } + ListElement{ radius: "65"; angle: "200"; value: "-39.8005"; } + ListElement{ radius: "65"; angle: "205"; value: "-43.8304"; } + ListElement{ radius: "65"; angle: "210"; value: "-47.6995"; } + ListElement{ radius: "65"; angle: "215"; value: "-51.3783"; } + ListElement{ radius: "65"; angle: "220"; value: "-54.8389"; } + ListElement{ radius: "65"; angle: "225"; value: "-58.0549"; } + ListElement{ radius: "65"; angle: "230"; value: "-61.0017"; } + ListElement{ radius: "65"; angle: "235"; value: "-63.6571"; } + ListElement{ radius: "65"; angle: "240"; value: "-66.0008"; } + ListElement{ radius: "65"; angle: "245"; value: "-68.0149"; } + ListElement{ radius: "65"; angle: "250"; value: "-69.6842"; } + ListElement{ radius: "65"; angle: "255"; value: "-70.9958"; } + ListElement{ radius: "65"; angle: "260"; value: "-71.9399"; } + ListElement{ radius: "65"; angle: "265"; value: "-72.5093"; } + ListElement{ radius: "65"; angle: "270"; value: "-72.6995"; } + ListElement{ radius: "65"; angle: "275"; value: "-72.5093"; } + ListElement{ radius: "65"; angle: "280"; value: "-71.9399"; } + ListElement{ radius: "65"; angle: "285"; value: "-70.9958"; } + ListElement{ radius: "65"; angle: "290"; value: "-69.6842"; } + ListElement{ radius: "65"; angle: "295"; value: "-68.0149"; } + ListElement{ radius: "65"; angle: "300"; value: "-66.0008"; } + ListElement{ radius: "65"; angle: "305"; value: "-63.6571"; } + ListElement{ radius: "65"; angle: "310"; value: "-61.0017"; } + ListElement{ radius: "65"; angle: "315"; value: "-58.0549"; } + ListElement{ radius: "65"; angle: "320"; value: "-54.8389"; } + ListElement{ radius: "65"; angle: "325"; value: "-51.3783"; } + ListElement{ radius: "65"; angle: "330"; value: "-47.6995"; } + ListElement{ radius: "65"; angle: "335"; value: "-43.8304"; } + ListElement{ radius: "65"; angle: "340"; value: "-39.8005"; } + ListElement{ radius: "65"; angle: "345"; value: "-35.6405"; } + ListElement{ radius: "65"; angle: "350"; value: "-31.3819"; } + ListElement{ radius: "65"; angle: "355"; value: "-27.0573"; } + ListElement{ radius: "65"; angle: "360"; value: "-22.6995"; } + ListElement{ radius: "70"; angle: "0"; value: "-29.3893"; } + ListElement{ radius: "70"; angle: "5"; value: "-25.0315"; } + ListElement{ radius: "70"; angle: "10"; value: "-20.7069"; } + ListElement{ radius: "70"; angle: "15"; value: "-16.4483"; } + ListElement{ radius: "70"; angle: "20"; value: "-12.2883"; } + ListElement{ radius: "70"; angle: "25"; value: "-8.25835"; } + ListElement{ radius: "70"; angle: "30"; value: "-4.38926"; } + ListElement{ radius: "70"; angle: "35"; value: "-0.710441"; } + ListElement{ radius: "70"; angle: "40"; value: "2.75012"; } + ListElement{ radius: "70"; angle: "45"; value: "5.96608"; } + ListElement{ radius: "70"; angle: "50"; value: "8.91296"; } + ListElement{ radius: "70"; angle: "55"; value: "11.5683"; } + ListElement{ radius: "70"; angle: "60"; value: "13.912"; } + ListElement{ radius: "70"; angle: "65"; value: "15.9261"; } + ListElement{ radius: "70"; angle: "70"; value: "17.5954"; } + ListElement{ radius: "70"; angle: "75"; value: "18.907"; } + ListElement{ radius: "70"; angle: "80"; value: "19.8511"; } + ListElement{ radius: "70"; angle: "85"; value: "20.4205"; } + ListElement{ radius: "70"; angle: "90"; value: "20.6107"; } + ListElement{ radius: "70"; angle: "95"; value: "20.4205"; } + ListElement{ radius: "70"; angle: "100"; value: "19.8511"; } + ListElement{ radius: "70"; angle: "105"; value: "18.907"; } + ListElement{ radius: "70"; angle: "110"; value: "17.5954"; } + ListElement{ radius: "70"; angle: "115"; value: "15.9261"; } + ListElement{ radius: "70"; angle: "120"; value: "13.912"; } + ListElement{ radius: "70"; angle: "125"; value: "11.5683"; } + ListElement{ radius: "70"; angle: "130"; value: "8.91296"; } + ListElement{ radius: "70"; angle: "135"; value: "5.96608"; } + ListElement{ radius: "70"; angle: "140"; value: "2.75012"; } + ListElement{ radius: "70"; angle: "145"; value: "-0.710441"; } + ListElement{ radius: "70"; angle: "150"; value: "-4.38926"; } + ListElement{ radius: "70"; angle: "155"; value: "-8.25835"; } + ListElement{ radius: "70"; angle: "160"; value: "-12.2883"; } + ListElement{ radius: "70"; angle: "165"; value: "-16.4483"; } + ListElement{ radius: "70"; angle: "170"; value: "-20.7069"; } + ListElement{ radius: "70"; angle: "175"; value: "-25.0315"; } + ListElement{ radius: "70"; angle: "180"; value: "-29.3893"; } + ListElement{ radius: "70"; angle: "185"; value: "-33.747"; } + ListElement{ radius: "70"; angle: "190"; value: "-38.0717"; } + ListElement{ radius: "70"; angle: "195"; value: "-42.3302"; } + ListElement{ radius: "70"; angle: "200"; value: "-46.4903"; } + ListElement{ radius: "70"; angle: "205"; value: "-50.5202"; } + ListElement{ radius: "70"; angle: "210"; value: "-54.3893"; } + ListElement{ radius: "70"; angle: "215"; value: "-58.0681"; } + ListElement{ radius: "70"; angle: "220"; value: "-61.5286"; } + ListElement{ radius: "70"; angle: "225"; value: "-64.7446"; } + ListElement{ radius: "70"; angle: "230"; value: "-67.6915"; } + ListElement{ radius: "70"; angle: "235"; value: "-70.3469"; } + ListElement{ radius: "70"; angle: "240"; value: "-72.6905"; } + ListElement{ radius: "70"; angle: "245"; value: "-74.7047"; } + ListElement{ radius: "70"; angle: "250"; value: "-76.3739"; } + ListElement{ radius: "70"; angle: "255"; value: "-77.6856"; } + ListElement{ radius: "70"; angle: "260"; value: "-78.6297"; } + ListElement{ radius: "70"; angle: "265"; value: "-79.199"; } + ListElement{ radius: "70"; angle: "270"; value: "-79.3893"; } + ListElement{ radius: "70"; angle: "275"; value: "-79.199"; } + ListElement{ radius: "70"; angle: "280"; value: "-78.6297"; } + ListElement{ radius: "70"; angle: "285"; value: "-77.6856"; } + ListElement{ radius: "70"; angle: "290"; value: "-76.3739"; } + ListElement{ radius: "70"; angle: "295"; value: "-74.7047"; } + ListElement{ radius: "70"; angle: "300"; value: "-72.6905"; } + ListElement{ radius: "70"; angle: "305"; value: "-70.3469"; } + ListElement{ radius: "70"; angle: "310"; value: "-67.6915"; } + ListElement{ radius: "70"; angle: "315"; value: "-64.7446"; } + ListElement{ radius: "70"; angle: "320"; value: "-61.5286"; } + ListElement{ radius: "70"; angle: "325"; value: "-58.0681"; } + ListElement{ radius: "70"; angle: "330"; value: "-54.3893"; } + ListElement{ radius: "70"; angle: "335"; value: "-50.5202"; } + ListElement{ radius: "70"; angle: "340"; value: "-46.4903"; } + ListElement{ radius: "70"; angle: "345"; value: "-42.3302"; } + ListElement{ radius: "70"; angle: "350"; value: "-38.0717"; } + ListElement{ radius: "70"; angle: "355"; value: "-33.747"; } + ListElement{ radius: "70"; angle: "360"; value: "-29.3893"; } + ListElement{ radius: "75"; angle: "0"; value: "-35.3553"; } + ListElement{ radius: "75"; angle: "5"; value: "-30.9976"; } + ListElement{ radius: "75"; angle: "10"; value: "-26.6729"; } + ListElement{ radius: "75"; angle: "15"; value: "-22.4144"; } + ListElement{ radius: "75"; angle: "20"; value: "-18.2543"; } + ListElement{ radius: "75"; angle: "25"; value: "-14.2244"; } + ListElement{ radius: "75"; angle: "30"; value: "-10.3553"; } + ListElement{ radius: "75"; angle: "35"; value: "-6.67652"; } + ListElement{ radius: "75"; angle: "40"; value: "-3.21596"; } + ListElement{ radius: "75"; angle: "45"; value: "5.55112e-15"; } + ListElement{ radius: "75"; angle: "50"; value: "2.94688"; } + ListElement{ radius: "75"; angle: "55"; value: "5.60226"; } + ListElement{ radius: "75"; angle: "60"; value: "7.94593"; } + ListElement{ radius: "75"; angle: "65"; value: "9.96005"; } + ListElement{ radius: "75"; angle: "70"; value: "11.6293"; } + ListElement{ radius: "75"; angle: "75"; value: "12.941"; } + ListElement{ radius: "75"; angle: "80"; value: "13.885"; } + ListElement{ radius: "75"; angle: "85"; value: "14.4544"; } + ListElement{ radius: "75"; angle: "90"; value: "14.6447"; } + ListElement{ radius: "75"; angle: "95"; value: "14.4544"; } + ListElement{ radius: "75"; angle: "100"; value: "13.885"; } + ListElement{ radius: "75"; angle: "105"; value: "12.941"; } + ListElement{ radius: "75"; angle: "110"; value: "11.6293"; } + ListElement{ radius: "75"; angle: "115"; value: "9.96005"; } + ListElement{ radius: "75"; angle: "120"; value: "7.94593"; } + ListElement{ radius: "75"; angle: "125"; value: "5.60226"; } + ListElement{ radius: "75"; angle: "130"; value: "2.94688"; } + ListElement{ radius: "75"; angle: "135"; value: "5.55112e-15"; } + ListElement{ radius: "75"; angle: "140"; value: "-3.21596"; } + ListElement{ radius: "75"; angle: "145"; value: "-6.67652"; } + ListElement{ radius: "75"; angle: "150"; value: "-10.3553"; } + ListElement{ radius: "75"; angle: "155"; value: "-14.2244"; } + ListElement{ radius: "75"; angle: "160"; value: "-18.2543"; } + ListElement{ radius: "75"; angle: "165"; value: "-22.4144"; } + ListElement{ radius: "75"; angle: "170"; value: "-26.6729"; } + ListElement{ radius: "75"; angle: "175"; value: "-30.9976"; } + ListElement{ radius: "75"; angle: "180"; value: "-35.3553"; } + ListElement{ radius: "75"; angle: "185"; value: "-39.7131"; } + ListElement{ radius: "75"; angle: "190"; value: "-44.0377"; } + ListElement{ radius: "75"; angle: "195"; value: "-48.2963"; } + ListElement{ radius: "75"; angle: "200"; value: "-52.4563"; } + ListElement{ radius: "75"; angle: "205"; value: "-56.4863"; } + ListElement{ radius: "75"; angle: "210"; value: "-60.3553"; } + ListElement{ radius: "75"; angle: "215"; value: "-64.0342"; } + ListElement{ radius: "75"; angle: "220"; value: "-67.4947"; } + ListElement{ radius: "75"; angle: "225"; value: "-70.7107"; } + ListElement{ radius: "75"; angle: "230"; value: "-73.6576"; } + ListElement{ radius: "75"; angle: "235"; value: "-76.3129"; } + ListElement{ radius: "75"; angle: "240"; value: "-78.6566"; } + ListElement{ radius: "75"; angle: "245"; value: "-80.6707"; } + ListElement{ radius: "75"; angle: "250"; value: "-82.34"; } + ListElement{ radius: "75"; angle: "255"; value: "-83.6516"; } + ListElement{ radius: "75"; angle: "260"; value: "-84.5957"; } + ListElement{ radius: "75"; angle: "265"; value: "-85.1651"; } + ListElement{ radius: "75"; angle: "270"; value: "-85.3553"; } + ListElement{ radius: "75"; angle: "275"; value: "-85.1651"; } + ListElement{ radius: "75"; angle: "280"; value: "-84.5957"; } + ListElement{ radius: "75"; angle: "285"; value: "-83.6516"; } + ListElement{ radius: "75"; angle: "290"; value: "-82.34"; } + ListElement{ radius: "75"; angle: "295"; value: "-80.6707"; } + ListElement{ radius: "75"; angle: "300"; value: "-78.6566"; } + ListElement{ radius: "75"; angle: "305"; value: "-76.3129"; } + ListElement{ radius: "75"; angle: "310"; value: "-73.6576"; } + ListElement{ radius: "75"; angle: "315"; value: "-70.7107"; } + ListElement{ radius: "75"; angle: "320"; value: "-67.4947"; } + ListElement{ radius: "75"; angle: "325"; value: "-64.0342"; } + ListElement{ radius: "75"; angle: "330"; value: "-60.3553"; } + ListElement{ radius: "75"; angle: "335"; value: "-56.4863"; } + ListElement{ radius: "75"; angle: "340"; value: "-52.4563"; } + ListElement{ radius: "75"; angle: "345"; value: "-48.2963"; } + ListElement{ radius: "75"; angle: "350"; value: "-44.0377"; } + ListElement{ radius: "75"; angle: "355"; value: "-39.7131"; } + ListElement{ radius: "75"; angle: "360"; value: "-35.3553"; } + ListElement{ radius: "80"; angle: "0"; value: "-40.4508"; } + ListElement{ radius: "80"; angle: "5"; value: "-36.0931"; } + ListElement{ radius: "80"; angle: "10"; value: "-31.7684"; } + ListElement{ radius: "80"; angle: "15"; value: "-27.5099"; } + ListElement{ radius: "80"; angle: "20"; value: "-23.3498"; } + ListElement{ radius: "80"; angle: "25"; value: "-19.3199"; } + ListElement{ radius: "80"; angle: "30"; value: "-15.4508"; } + ListElement{ radius: "80"; angle: "35"; value: "-11.772"; } + ListElement{ radius: "80"; angle: "40"; value: "-8.31147"; } + ListElement{ radius: "80"; angle: "45"; value: "-5.09551"; } + ListElement{ radius: "80"; angle: "50"; value: "-2.14863"; } + ListElement{ radius: "80"; angle: "55"; value: "0.506752"; } + ListElement{ radius: "80"; angle: "60"; value: "2.85042"; } + ListElement{ radius: "80"; angle: "65"; value: "4.86454"; } + ListElement{ radius: "80"; angle: "70"; value: "6.53378"; } + ListElement{ radius: "80"; angle: "75"; value: "7.84544"; } + ListElement{ radius: "80"; angle: "80"; value: "8.78954"; } + ListElement{ radius: "80"; angle: "85"; value: "9.35889"; } + ListElement{ radius: "80"; angle: "90"; value: "9.54915"; } + ListElement{ radius: "80"; angle: "95"; value: "9.35889"; } + ListElement{ radius: "80"; angle: "100"; value: "8.78954"; } + ListElement{ radius: "80"; angle: "105"; value: "7.84544"; } + ListElement{ radius: "80"; angle: "110"; value: "6.53378"; } + ListElement{ radius: "80"; angle: "115"; value: "4.86454"; } + ListElement{ radius: "80"; angle: "120"; value: "2.85042"; } + ListElement{ radius: "80"; angle: "125"; value: "0.506752"; } + ListElement{ radius: "80"; angle: "130"; value: "-2.14863"; } + ListElement{ radius: "80"; angle: "135"; value: "-5.09551"; } + ListElement{ radius: "80"; angle: "140"; value: "-8.31147"; } + ListElement{ radius: "80"; angle: "145"; value: "-11.772"; } + ListElement{ radius: "80"; angle: "150"; value: "-15.4508"; } + ListElement{ radius: "80"; angle: "155"; value: "-19.3199"; } + ListElement{ radius: "80"; angle: "160"; value: "-23.3498"; } + ListElement{ radius: "80"; angle: "165"; value: "-27.5099"; } + ListElement{ radius: "80"; angle: "170"; value: "-31.7684"; } + ListElement{ radius: "80"; angle: "175"; value: "-36.0931"; } + ListElement{ radius: "80"; angle: "180"; value: "-40.4508"; } + ListElement{ radius: "80"; angle: "185"; value: "-44.8086"; } + ListElement{ radius: "80"; angle: "190"; value: "-49.1333"; } + ListElement{ radius: "80"; angle: "195"; value: "-53.3918"; } + ListElement{ radius: "80"; angle: "200"; value: "-57.5519"; } + ListElement{ radius: "80"; angle: "205"; value: "-61.5818"; } + ListElement{ radius: "80"; angle: "210"; value: "-65.4508"; } + ListElement{ radius: "80"; angle: "215"; value: "-69.1297"; } + ListElement{ radius: "80"; angle: "220"; value: "-72.5902"; } + ListElement{ radius: "80"; angle: "225"; value: "-75.8062"; } + ListElement{ radius: "80"; angle: "230"; value: "-78.7531"; } + ListElement{ radius: "80"; angle: "235"; value: "-81.4085"; } + ListElement{ radius: "80"; angle: "240"; value: "-83.7521"; } + ListElement{ radius: "80"; angle: "245"; value: "-85.7662"; } + ListElement{ radius: "80"; angle: "250"; value: "-87.4355"; } + ListElement{ radius: "80"; angle: "255"; value: "-88.7471"; } + ListElement{ radius: "80"; angle: "260"; value: "-89.6912"; } + ListElement{ radius: "80"; angle: "265"; value: "-90.2606"; } + ListElement{ radius: "80"; angle: "270"; value: "-90.4508"; } + ListElement{ radius: "80"; angle: "275"; value: "-90.2606"; } + ListElement{ radius: "80"; angle: "280"; value: "-89.6912"; } + ListElement{ radius: "80"; angle: "285"; value: "-88.7471"; } + ListElement{ radius: "80"; angle: "290"; value: "-87.4355"; } + ListElement{ radius: "80"; angle: "295"; value: "-85.7662"; } + ListElement{ radius: "80"; angle: "300"; value: "-83.7521"; } + ListElement{ radius: "80"; angle: "305"; value: "-81.4085"; } + ListElement{ radius: "80"; angle: "310"; value: "-78.7531"; } + ListElement{ radius: "80"; angle: "315"; value: "-75.8062"; } + ListElement{ radius: "80"; angle: "320"; value: "-72.5902"; } + ListElement{ radius: "80"; angle: "325"; value: "-69.1297"; } + ListElement{ radius: "80"; angle: "330"; value: "-65.4508"; } + ListElement{ radius: "80"; angle: "335"; value: "-61.5818"; } + ListElement{ radius: "80"; angle: "340"; value: "-57.5519"; } + ListElement{ radius: "80"; angle: "345"; value: "-53.3918"; } + ListElement{ radius: "80"; angle: "350"; value: "-49.1333"; } + ListElement{ radius: "80"; angle: "355"; value: "-44.8086"; } + ListElement{ radius: "80"; angle: "360"; value: "-40.4508"; } + ListElement{ radius: "85"; angle: "0"; value: "-44.5503"; } + ListElement{ radius: "85"; angle: "5"; value: "-40.1925"; } + ListElement{ radius: "85"; angle: "10"; value: "-35.8679"; } + ListElement{ radius: "85"; angle: "15"; value: "-31.6094"; } + ListElement{ radius: "85"; angle: "20"; value: "-27.4493"; } + ListElement{ radius: "85"; angle: "25"; value: "-23.4194"; } + ListElement{ radius: "85"; angle: "30"; value: "-19.5503"; } + ListElement{ radius: "85"; angle: "35"; value: "-15.8715"; } + ListElement{ radius: "85"; angle: "40"; value: "-12.4109"; } + ListElement{ radius: "85"; angle: "45"; value: "-9.19499"; } + ListElement{ radius: "85"; angle: "50"; value: "-6.2481"; } + ListElement{ radius: "85"; angle: "55"; value: "-3.59272"; } + ListElement{ radius: "85"; angle: "60"; value: "-1.24906"; } + ListElement{ radius: "85"; angle: "65"; value: "0.765063"; } + ListElement{ radius: "85"; angle: "70"; value: "2.4343"; } + ListElement{ radius: "85"; angle: "75"; value: "3.74597"; } + ListElement{ radius: "85"; angle: "80"; value: "4.69006"; } + ListElement{ radius: "85"; angle: "85"; value: "5.25941"; } + ListElement{ radius: "85"; angle: "90"; value: "5.44967"; } + ListElement{ radius: "85"; angle: "95"; value: "5.25941"; } + ListElement{ radius: "85"; angle: "100"; value: "4.69006"; } + ListElement{ radius: "85"; angle: "105"; value: "3.74597"; } + ListElement{ radius: "85"; angle: "110"; value: "2.4343"; } + ListElement{ radius: "85"; angle: "115"; value: "0.765063"; } + ListElement{ radius: "85"; angle: "120"; value: "-1.24906"; } + ListElement{ radius: "85"; angle: "125"; value: "-3.59272"; } + ListElement{ radius: "85"; angle: "130"; value: "-6.2481"; } + ListElement{ radius: "85"; angle: "135"; value: "-9.19499"; } + ListElement{ radius: "85"; angle: "140"; value: "-12.4109"; } + ListElement{ radius: "85"; angle: "145"; value: "-15.8715"; } + ListElement{ radius: "85"; angle: "150"; value: "-19.5503"; } + ListElement{ radius: "85"; angle: "155"; value: "-23.4194"; } + ListElement{ radius: "85"; angle: "160"; value: "-27.4493"; } + ListElement{ radius: "85"; angle: "165"; value: "-31.6094"; } + ListElement{ radius: "85"; angle: "170"; value: "-35.8679"; } + ListElement{ radius: "85"; angle: "175"; value: "-40.1925"; } + ListElement{ radius: "85"; angle: "180"; value: "-44.5503"; } + ListElement{ radius: "85"; angle: "185"; value: "-48.9081"; } + ListElement{ radius: "85"; angle: "190"; value: "-53.2327"; } + ListElement{ radius: "85"; angle: "195"; value: "-57.4913"; } + ListElement{ radius: "85"; angle: "200"; value: "-61.6513"; } + ListElement{ radius: "85"; angle: "205"; value: "-65.6812"; } + ListElement{ radius: "85"; angle: "210"; value: "-69.5503"; } + ListElement{ radius: "85"; angle: "215"; value: "-73.2291"; } + ListElement{ radius: "85"; angle: "220"; value: "-76.6897"; } + ListElement{ radius: "85"; angle: "225"; value: "-79.9057"; } + ListElement{ radius: "85"; angle: "230"; value: "-82.8525"; } + ListElement{ radius: "85"; angle: "235"; value: "-85.5079"; } + ListElement{ radius: "85"; angle: "240"; value: "-87.8516"; } + ListElement{ radius: "85"; angle: "245"; value: "-89.8657"; } + ListElement{ radius: "85"; angle: "250"; value: "-91.535"; } + ListElement{ radius: "85"; angle: "255"; value: "-92.8466"; } + ListElement{ radius: "85"; angle: "260"; value: "-93.7907"; } + ListElement{ radius: "85"; angle: "265"; value: "-94.3601"; } + ListElement{ radius: "85"; angle: "270"; value: "-94.5503"; } + ListElement{ radius: "85"; angle: "275"; value: "-94.3601"; } + ListElement{ radius: "85"; angle: "280"; value: "-93.7907"; } + ListElement{ radius: "85"; angle: "285"; value: "-92.8466"; } + ListElement{ radius: "85"; angle: "290"; value: "-91.535"; } + ListElement{ radius: "85"; angle: "295"; value: "-89.8657"; } + ListElement{ radius: "85"; angle: "300"; value: "-87.8516"; } + ListElement{ radius: "85"; angle: "305"; value: "-85.5079"; } + ListElement{ radius: "85"; angle: "310"; value: "-82.8525"; } + ListElement{ radius: "85"; angle: "315"; value: "-79.9057"; } + ListElement{ radius: "85"; angle: "320"; value: "-76.6897"; } + ListElement{ radius: "85"; angle: "325"; value: "-73.2291"; } + ListElement{ radius: "85"; angle: "330"; value: "-69.5503"; } + ListElement{ radius: "85"; angle: "335"; value: "-65.6812"; } + ListElement{ radius: "85"; angle: "340"; value: "-61.6513"; } + ListElement{ radius: "85"; angle: "345"; value: "-57.4913"; } + ListElement{ radius: "85"; angle: "350"; value: "-53.2327"; } + ListElement{ radius: "85"; angle: "355"; value: "-48.9081"; } + ListElement{ radius: "85"; angle: "360"; value: "-44.5503"; } + ListElement{ radius: "90"; angle: "0"; value: "-47.5528"; } + ListElement{ radius: "90"; angle: "5"; value: "-43.195"; } + ListElement{ radius: "90"; angle: "10"; value: "-38.8704"; } + ListElement{ radius: "90"; angle: "15"; value: "-34.6119"; } + ListElement{ radius: "90"; angle: "20"; value: "-30.4518"; } + ListElement{ radius: "90"; angle: "25"; value: "-26.4219"; } + ListElement{ radius: "90"; angle: "30"; value: "-22.5528"; } + ListElement{ radius: "90"; angle: "35"; value: "-18.874"; } + ListElement{ radius: "90"; angle: "40"; value: "-15.4134"; } + ListElement{ radius: "90"; angle: "45"; value: "-12.1975"; } + ListElement{ radius: "90"; angle: "50"; value: "-9.2506"; } + ListElement{ radius: "90"; angle: "55"; value: "-6.59522"; } + ListElement{ radius: "90"; angle: "60"; value: "-4.25156"; } + ListElement{ radius: "90"; angle: "65"; value: "-2.23744"; } + ListElement{ radius: "90"; angle: "70"; value: "-0.568195"; } + ListElement{ radius: "90"; angle: "75"; value: "0.743465"; } + ListElement{ radius: "90"; angle: "80"; value: "1.68756"; } + ListElement{ radius: "90"; angle: "85"; value: "2.25691"; } + ListElement{ radius: "90"; angle: "90"; value: "2.44717"; } + ListElement{ radius: "90"; angle: "95"; value: "2.25691"; } + ListElement{ radius: "90"; angle: "100"; value: "1.68756"; } + ListElement{ radius: "90"; angle: "105"; value: "0.743465"; } + ListElement{ radius: "90"; angle: "110"; value: "-0.568195"; } + ListElement{ radius: "90"; angle: "115"; value: "-2.23744"; } + ListElement{ radius: "90"; angle: "120"; value: "-4.25156"; } + ListElement{ radius: "90"; angle: "125"; value: "-6.59522"; } + ListElement{ radius: "90"; angle: "130"; value: "-9.2506"; } + ListElement{ radius: "90"; angle: "135"; value: "-12.1975"; } + ListElement{ radius: "90"; angle: "140"; value: "-15.4134"; } + ListElement{ radius: "90"; angle: "145"; value: "-18.874"; } + ListElement{ radius: "90"; angle: "150"; value: "-22.5528"; } + ListElement{ radius: "90"; angle: "155"; value: "-26.4219"; } + ListElement{ radius: "90"; angle: "160"; value: "-30.4518"; } + ListElement{ radius: "90"; angle: "165"; value: "-34.6119"; } + ListElement{ radius: "90"; angle: "170"; value: "-38.8704"; } + ListElement{ radius: "90"; angle: "175"; value: "-43.195"; } + ListElement{ radius: "90"; angle: "180"; value: "-47.5528"; } + ListElement{ radius: "90"; angle: "185"; value: "-51.9106"; } + ListElement{ radius: "90"; angle: "190"; value: "-56.2352"; } + ListElement{ radius: "90"; angle: "195"; value: "-60.4938"; } + ListElement{ radius: "90"; angle: "200"; value: "-64.6538"; } + ListElement{ radius: "90"; angle: "205"; value: "-68.6837"; } + ListElement{ radius: "90"; angle: "210"; value: "-72.5528"; } + ListElement{ radius: "90"; angle: "215"; value: "-76.2316"; } + ListElement{ radius: "90"; angle: "220"; value: "-79.6922"; } + ListElement{ radius: "90"; angle: "225"; value: "-82.9082"; } + ListElement{ radius: "90"; angle: "230"; value: "-85.855"; } + ListElement{ radius: "90"; angle: "235"; value: "-88.5104"; } + ListElement{ radius: "90"; angle: "240"; value: "-90.8541"; } + ListElement{ radius: "90"; angle: "245"; value: "-92.8682"; } + ListElement{ radius: "90"; angle: "250"; value: "-94.5375"; } + ListElement{ radius: "90"; angle: "255"; value: "-95.8491"; } + ListElement{ radius: "90"; angle: "260"; value: "-96.7932"; } + ListElement{ radius: "90"; angle: "265"; value: "-97.3626"; } + ListElement{ radius: "90"; angle: "270"; value: "-97.5528"; } + ListElement{ radius: "90"; angle: "275"; value: "-97.3626"; } + ListElement{ radius: "90"; angle: "280"; value: "-96.7932"; } + ListElement{ radius: "90"; angle: "285"; value: "-95.8491"; } + ListElement{ radius: "90"; angle: "290"; value: "-94.5375"; } + ListElement{ radius: "90"; angle: "295"; value: "-92.8682"; } + ListElement{ radius: "90"; angle: "300"; value: "-90.8541"; } + ListElement{ radius: "90"; angle: "305"; value: "-88.5104"; } + ListElement{ radius: "90"; angle: "310"; value: "-85.855"; } + ListElement{ radius: "90"; angle: "315"; value: "-82.9082"; } + ListElement{ radius: "90"; angle: "320"; value: "-79.6922"; } + ListElement{ radius: "90"; angle: "325"; value: "-76.2316"; } + ListElement{ radius: "90"; angle: "330"; value: "-72.5528"; } + ListElement{ radius: "90"; angle: "335"; value: "-68.6837"; } + ListElement{ radius: "90"; angle: "340"; value: "-64.6538"; } + ListElement{ radius: "90"; angle: "345"; value: "-60.4938"; } + ListElement{ radius: "90"; angle: "350"; value: "-56.2352"; } + ListElement{ radius: "90"; angle: "355"; value: "-51.9106"; } + ListElement{ radius: "90"; angle: "360"; value: "-47.5528"; } + ListElement{ radius: "95"; angle: "0"; value: "-49.3844"; } + ListElement{ radius: "95"; angle: "5"; value: "-45.0266"; } + ListElement{ radius: "95"; angle: "10"; value: "-40.702"; } + ListElement{ radius: "95"; angle: "15"; value: "-36.4435"; } + ListElement{ radius: "95"; angle: "20"; value: "-32.2834"; } + ListElement{ radius: "95"; angle: "25"; value: "-28.2535"; } + ListElement{ radius: "95"; angle: "30"; value: "-24.3844"; } + ListElement{ radius: "95"; angle: "35"; value: "-20.7056"; } + ListElement{ radius: "95"; angle: "40"; value: "-17.245"; } + ListElement{ radius: "95"; angle: "45"; value: "-14.0291"; } + ListElement{ radius: "95"; angle: "50"; value: "-11.0822"; } + ListElement{ radius: "95"; angle: "55"; value: "-8.42681"; } + ListElement{ radius: "95"; angle: "60"; value: "-6.08315"; } + ListElement{ radius: "95"; angle: "65"; value: "-4.06903"; } + ListElement{ radius: "95"; angle: "70"; value: "-2.39979"; } + ListElement{ radius: "95"; angle: "75"; value: "-1.08813"; } + ListElement{ radius: "95"; angle: "80"; value: "-0.144029"; } + ListElement{ radius: "95"; angle: "85"; value: "0.425318"; } + ListElement{ radius: "95"; angle: "90"; value: "0.615583"; } + ListElement{ radius: "95"; angle: "95"; value: "0.425318"; } + ListElement{ radius: "95"; angle: "100"; value: "-0.144029"; } + ListElement{ radius: "95"; angle: "105"; value: "-1.08813"; } + ListElement{ radius: "95"; angle: "110"; value: "-2.39979"; } + ListElement{ radius: "95"; angle: "115"; value: "-4.06903"; } + ListElement{ radius: "95"; angle: "120"; value: "-6.08315"; } + ListElement{ radius: "95"; angle: "125"; value: "-8.42681"; } + ListElement{ radius: "95"; angle: "130"; value: "-11.0822"; } + ListElement{ radius: "95"; angle: "135"; value: "-14.0291"; } + ListElement{ radius: "95"; angle: "140"; value: "-17.245"; } + ListElement{ radius: "95"; angle: "145"; value: "-20.7056"; } + ListElement{ radius: "95"; angle: "150"; value: "-24.3844"; } + ListElement{ radius: "95"; angle: "155"; value: "-28.2535"; } + ListElement{ radius: "95"; angle: "160"; value: "-32.2834"; } + ListElement{ radius: "95"; angle: "165"; value: "-36.4435"; } + ListElement{ radius: "95"; angle: "170"; value: "-40.702"; } + ListElement{ radius: "95"; angle: "175"; value: "-45.0266"; } + ListElement{ radius: "95"; angle: "180"; value: "-49.3844"; } + ListElement{ radius: "95"; angle: "185"; value: "-53.7422"; } + ListElement{ radius: "95"; angle: "190"; value: "-58.0668"; } + ListElement{ radius: "95"; angle: "195"; value: "-62.3254"; } + ListElement{ radius: "95"; angle: "200"; value: "-66.4854"; } + ListElement{ radius: "95"; angle: "205"; value: "-70.5153"; } + ListElement{ radius: "95"; angle: "210"; value: "-74.3844"; } + ListElement{ radius: "95"; angle: "215"; value: "-78.0632"; } + ListElement{ radius: "95"; angle: "220"; value: "-81.5238"; } + ListElement{ radius: "95"; angle: "225"; value: "-84.7398"; } + ListElement{ radius: "95"; angle: "230"; value: "-87.6866"; } + ListElement{ radius: "95"; angle: "235"; value: "-90.342"; } + ListElement{ radius: "95"; angle: "240"; value: "-92.6857"; } + ListElement{ radius: "95"; angle: "245"; value: "-94.6998"; } + ListElement{ radius: "95"; angle: "250"; value: "-96.369"; } + ListElement{ radius: "95"; angle: "255"; value: "-97.6807"; } + ListElement{ radius: "95"; angle: "260"; value: "-98.6248"; } + ListElement{ radius: "95"; angle: "265"; value: "-99.1942"; } + ListElement{ radius: "95"; angle: "270"; value: "-99.3844"; } + ListElement{ radius: "95"; angle: "275"; value: "-99.1942"; } + ListElement{ radius: "95"; angle: "280"; value: "-98.6248"; } + ListElement{ radius: "95"; angle: "285"; value: "-97.6807"; } + ListElement{ radius: "95"; angle: "290"; value: "-96.369"; } + ListElement{ radius: "95"; angle: "295"; value: "-94.6998"; } + ListElement{ radius: "95"; angle: "300"; value: "-92.6857"; } + ListElement{ radius: "95"; angle: "305"; value: "-90.342"; } + ListElement{ radius: "95"; angle: "310"; value: "-87.6866"; } + ListElement{ radius: "95"; angle: "315"; value: "-84.7398"; } + ListElement{ radius: "95"; angle: "320"; value: "-81.5238"; } + ListElement{ radius: "95"; angle: "325"; value: "-78.0632"; } + ListElement{ radius: "95"; angle: "330"; value: "-74.3844"; } + ListElement{ radius: "95"; angle: "335"; value: "-70.5153"; } + ListElement{ radius: "95"; angle: "340"; value: "-66.4854"; } + ListElement{ radius: "95"; angle: "345"; value: "-62.3254"; } + ListElement{ radius: "95"; angle: "350"; value: "-58.0668"; } + ListElement{ radius: "95"; angle: "355"; value: "-53.7422"; } + ListElement{ radius: "95"; angle: "360"; value: "-49.3844"; } + ListElement{ radius: "100"; angle: "0"; value: "-50"; } + ListElement{ radius: "100"; angle: "5"; value: "-45.6422"; } + ListElement{ radius: "100"; angle: "10"; value: "-41.3176"; } + ListElement{ radius: "100"; angle: "15"; value: "-37.059"; } + ListElement{ radius: "100"; angle: "20"; value: "-32.899"; } + ListElement{ radius: "100"; angle: "25"; value: "-28.8691"; } + ListElement{ radius: "100"; angle: "30"; value: "-25"; } + ListElement{ radius: "100"; angle: "35"; value: "-21.3212"; } + ListElement{ radius: "100"; angle: "40"; value: "-17.8606"; } + ListElement{ radius: "100"; angle: "45"; value: "-14.6447"; } + ListElement{ radius: "100"; angle: "50"; value: "-11.6978"; } + ListElement{ radius: "100"; angle: "55"; value: "-9.0424"; } + ListElement{ radius: "100"; angle: "60"; value: "-6.69873"; } + ListElement{ radius: "100"; angle: "65"; value: "-4.68461"; } + ListElement{ radius: "100"; angle: "70"; value: "-3.01537"; } + ListElement{ radius: "100"; angle: "75"; value: "-1.70371"; } + ListElement{ radius: "100"; angle: "80"; value: "-0.759612"; } + ListElement{ radius: "100"; angle: "85"; value: "-0.190265"; } + ListElement{ radius: "100"; angle: "90"; value: "0"; } + ListElement{ radius: "100"; angle: "95"; value: "-0.190265"; } + ListElement{ radius: "100"; angle: "100"; value: "-0.759612"; } + ListElement{ radius: "100"; angle: "105"; value: "-1.70371"; } + ListElement{ radius: "100"; angle: "110"; value: "-3.01537"; } + ListElement{ radius: "100"; angle: "115"; value: "-4.68461"; } + ListElement{ radius: "100"; angle: "120"; value: "-6.69873"; } + ListElement{ radius: "100"; angle: "125"; value: "-9.0424"; } + ListElement{ radius: "100"; angle: "130"; value: "-11.6978"; } + ListElement{ radius: "100"; angle: "135"; value: "-14.6447"; } + ListElement{ radius: "100"; angle: "140"; value: "-17.8606"; } + ListElement{ radius: "100"; angle: "145"; value: "-21.3212"; } + ListElement{ radius: "100"; angle: "150"; value: "-25"; } + ListElement{ radius: "100"; angle: "155"; value: "-28.8691"; } + ListElement{ radius: "100"; angle: "160"; value: "-32.899"; } + ListElement{ radius: "100"; angle: "165"; value: "-37.059"; } + ListElement{ radius: "100"; angle: "170"; value: "-41.3176"; } + ListElement{ radius: "100"; angle: "175"; value: "-45.6422"; } + ListElement{ radius: "100"; angle: "180"; value: "-50"; } + ListElement{ radius: "100"; angle: "185"; value: "-54.3578"; } + ListElement{ radius: "100"; angle: "190"; value: "-58.6824"; } + ListElement{ radius: "100"; angle: "195"; value: "-62.941"; } + ListElement{ radius: "100"; angle: "200"; value: "-67.101"; } + ListElement{ radius: "100"; angle: "205"; value: "-71.1309"; } + ListElement{ radius: "100"; angle: "210"; value: "-75"; } + ListElement{ radius: "100"; angle: "215"; value: "-78.6788"; } + ListElement{ radius: "100"; angle: "220"; value: "-82.1394"; } + ListElement{ radius: "100"; angle: "225"; value: "-85.3553"; } + ListElement{ radius: "100"; angle: "230"; value: "-88.3022"; } + ListElement{ radius: "100"; angle: "235"; value: "-90.9576"; } + ListElement{ radius: "100"; angle: "240"; value: "-93.3013"; } + ListElement{ radius: "100"; angle: "245"; value: "-95.3154"; } + ListElement{ radius: "100"; angle: "250"; value: "-96.9846"; } + ListElement{ radius: "100"; angle: "255"; value: "-98.2963"; } + ListElement{ radius: "100"; angle: "260"; value: "-99.2404"; } + ListElement{ radius: "100"; angle: "265"; value: "-99.8097"; } + ListElement{ radius: "100"; angle: "270"; value: "-100"; } + ListElement{ radius: "100"; angle: "275"; value: "-99.8097"; } + ListElement{ radius: "100"; angle: "280"; value: "-99.2404"; } + ListElement{ radius: "100"; angle: "285"; value: "-98.2963"; } + ListElement{ radius: "100"; angle: "290"; value: "-96.9846"; } + ListElement{ radius: "100"; angle: "295"; value: "-95.3154"; } + ListElement{ radius: "100"; angle: "300"; value: "-93.3013"; } + ListElement{ radius: "100"; angle: "305"; value: "-90.9576"; } + ListElement{ radius: "100"; angle: "310"; value: "-88.3022"; } + ListElement{ radius: "100"; angle: "315"; value: "-85.3553"; } + ListElement{ radius: "100"; angle: "320"; value: "-82.1394"; } + ListElement{ radius: "100"; angle: "325"; value: "-78.6788"; } + ListElement{ radius: "100"; angle: "330"; value: "-75"; } + ListElement{ radius: "100"; angle: "335"; value: "-71.1309"; } + ListElement{ radius: "100"; angle: "340"; value: "-67.101"; } + ListElement{ radius: "100"; angle: "345"; value: "-62.941"; } + ListElement{ radius: "100"; angle: "350"; value: "-58.6824"; } + ListElement{ radius: "100"; angle: "355"; value: "-54.3578"; } + ListElement{ radius: "100"; angle: "360"; value: "-50"; } + } +} diff --git a/examples/datavisualization/qmlspectrogram/qml/qmlspectrogram/NewButton.qml b/examples/datavisualization/qmlspectrogram/qml/qmlspectrogram/NewButton.qml new file mode 100644 index 00000000..e4fb99d2 --- /dev/null +++ b/examples/datavisualization/qmlspectrogram/qml/qmlspectrogram/NewButton.qml @@ -0,0 +1,52 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +import QtQuick 2.1 +import QtQuick.Controls 1.0 +import QtQuick.Controls.Styles 1.0 + +Item { + id: newbutton + + property alias text: buttonText.text + + signal clicked + + implicitWidth: buttonText.implicitWidth + 5 + implicitHeight: buttonText.implicitHeight + 10 + + Button { + id: buttonText + width: parent.width + height: parent.height + + style: ButtonStyle { + label: Component { + Text { + text: buttonText.text + clip: true + wrapMode: Text.WordWrap + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + anchors.fill: parent + } + } + } + onClicked: newbutton.clicked() + } +} diff --git a/examples/datavisualization/qmlspectrogram/qml/qmlspectrogram/main.qml b/examples/datavisualization/qmlspectrogram/qml/qmlspectrogram/main.qml new file mode 100644 index 00000000..0b02c058 --- /dev/null +++ b/examples/datavisualization/qmlspectrogram/qml/qmlspectrogram/main.qml @@ -0,0 +1,239 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +import QtQuick 2.1 +import QtQuick.Layouts 1.0 +import QtQuick.Window 2.1 +import QtDataVisualization 1.2 +import "." + +Window { + id: mainview + title: "Qt Quick 2 Spectrogram Example" + visible: true + width: 1024 + height: 768 + color: surfaceGraph.theme.windowColor + + Data { + id: surfaceData + } + + Item { + id: surfaceView + width: mainview.width + height: mainview.height + anchors.top: mainview.top + anchors.left: mainview.left + + //! [0] + ColorGradient { + id: surfaceGradient + ColorGradientStop { position: 0.0; color: "black" } + ColorGradientStop { position: 0.2; color: "red" } + ColorGradientStop { position: 0.5; color: "blue" } + ColorGradientStop { position: 0.8; color: "yellow" } + ColorGradientStop { position: 1.0; color: "white" } + } + //! [0] + + Surface3D { + id: surfaceGraph + width: surfaceView.width + height: surfaceView.height + + shadowQuality: AbstractGraph3D.ShadowQualityNone + selectionMode: AbstractGraph3D.SelectionSlice | AbstractGraph3D.SelectionItemAndRow + scene.activeCamera.cameraPreset: Camera3D.CameraPresetDirectlyAbove + axisX.segmentCount: 8 + axisX.subSegmentCount: 2 + axisX.labelFormat: "%i" + axisZ.segmentCount: 5 + axisZ.subSegmentCount: 2 + axisZ.labelFormat: "%i" + axisY.segmentCount: 8 + axisY.subSegmentCount: 1 + axisY.labelFormat: "%i \%" + axisY.title: "Value" + axisX.title: "Angle" + axisZ.title: "Radius" + + // Don't show specular spotlight as we don't want it to distort the colors + theme.lightStrength: 0.0 + theme.ambientLightStrength: 0.9 + theme.backgroundEnabled: false + + orthoProjection: true + + Surface3DSeries { + id: surfaceSeries + flatShadingEnabled: false + drawMode: Surface3DSeries.DrawSurface + baseGradient: surfaceGradient + colorStyle: Theme3D.ColorStyleRangeGradient + + ItemModelSurfaceDataProxy { + itemModel: surfaceData.model + rowRole: "radius" + columnRole: "angle" + yPosRole: "value" + } + } + } + } + + RowLayout { + id: buttonLayout + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + opacity: 0.5 + + NewButton { + id: polarToggle + Layout.fillWidth: true + Layout.fillHeight: true + text: "Switch to Polar" + //! [1] + onClicked: { + if (surfaceGraph.polar === false) { + surfaceGraph.polar = true + text = "Switch to Cartesian" + } else { + surfaceGraph.polar = false + text = "Switch to Polar" + } + } + //! [1] + } + + NewButton { + id: orthoToggle + Layout.fillWidth: true + Layout.fillHeight: true + text: "Switch to perspective" + onClicked: { + if (surfaceGraph.orthoProjection === true) { + surfaceGraph.orthoProjection = false; + text = "Switch to orthogonal" + } else { + surfaceGraph.orthoProjection = true; + surfaceGraph.scene.activeCamera.cameraPreset = Camera3D.CameraPresetDirectlyAbove + text = "Switch to perspective" + } + } + } + + NewButton { + id: surfaceGridToggle + Layout.fillWidth: true + Layout.fillHeight: true + text: "Toggle surface grid" + onClicked: { + if (surfaceSeries.drawMode & Surface3DSeries.DrawWireframe) { + surfaceSeries.drawMode &= ~Surface3DSeries.DrawWireframe; + } else { + surfaceSeries.drawMode |= Surface3DSeries.DrawWireframe; + } + } + } + + NewButton { + id: gridToggle + Layout.fillWidth: true + Layout.fillHeight: true + text: "Toggle axis grid" + onClicked: { + if (surfaceGraph.theme.gridEnabled === true) { + surfaceGraph.theme.gridEnabled = false; + } else { + surfaceGraph.theme.gridEnabled = true; + } + } + } + + NewButton { + id: gridOffsetToggle + Layout.fillWidth: true + Layout.fillHeight: true + text: "Toggle grid position" + onClicked: { + // TODO + if (surfaceGraph.theme.backgroundEnabled === true) { + } else { + surfaceGraph.theme.backgroundEnabled = true; + } + } + } + + NewButton { + id: labelOffsetToggle + Layout.fillWidth: true + Layout.fillHeight: true + text: "Toggle radial label position" + //visible: surfaceGraph.polar + onClicked: { + // TODO + if (surfaceGraph.theme.backgroundEnabled === true) { + } else { + surfaceGraph.theme.backgroundEnabled = true; + } + } + } + } + + Rectangle { + id: legend + anchors.margins: 20 + anchors.bottom: parent.bottom + anchors.top: buttonLayout.bottom + anchors.left: parent.left + border.color: "black" + border.width: 1 + width: 50 + rotation: 180 + gradient: Gradient { + GradientStop { position: 0.0; color: "black" } + GradientStop { position: 0.2; color: "red" } + GradientStop { position: 0.5; color: "blue" } + GradientStop { position: 0.8; color: "yellow" } + GradientStop { position: 1.0; color: "white" } + } + } + + Text { + anchors.verticalCenter: legend.bottom + anchors.left: legend.right + anchors.margins: 2 + text: surfaceGraph.axisY.min + "%" + } + + Text { + anchors.verticalCenter: legend.verticalCenter + anchors.left: legend.right + anchors.margins: 2 + text: (surfaceGraph.axisY.max + surfaceGraph.axisY.min) / 2 + "%" + } + + Text { + anchors.verticalCenter: legend.top + anchors.left: legend.right + anchors.margins: 2 + text: surfaceGraph.axisY.max + "%" + } +} diff --git a/examples/datavisualization/qmlspectrogram/qmlspectrogram.pro b/examples/datavisualization/qmlspectrogram/qmlspectrogram.pro new file mode 100644 index 00000000..655fb0b8 --- /dev/null +++ b/examples/datavisualization/qmlspectrogram/qmlspectrogram.pro @@ -0,0 +1,12 @@ +!include( ../examples.pri ) { + error( "Couldn't find the examples.pri file!" ) +} + +# The .cpp file which was generated for your project. Feel free to hack it. +SOURCES += main.cpp + +RESOURCES += qmlspectrogram.qrc + +OTHER_FILES += doc/src/* \ + doc/images/* \ + qml/qmlspectrogram/* diff --git a/examples/datavisualization/qmlspectrogram/qmlspectrogram.qrc b/examples/datavisualization/qmlspectrogram/qmlspectrogram.qrc new file mode 100644 index 00000000..9f024404 --- /dev/null +++ b/examples/datavisualization/qmlspectrogram/qmlspectrogram.qrc @@ -0,0 +1,7 @@ + + + qml/qmlspectrogram/Data.qml + qml/qmlspectrogram/main.qml + qml/qmlspectrogram/NewButton.qml + + diff --git a/src/datavisualization/doc/src/qtdatavisualization-qml-abstractdeclarative.qdoc b/src/datavisualization/doc/src/qtdatavisualization-qml-abstractdeclarative.qdoc index 2cc3eece..5afbc8c7 100644 --- a/src/datavisualization/doc/src/qtdatavisualization-qml-abstractdeclarative.qdoc +++ b/src/datavisualization/doc/src/qtdatavisualization-qml-abstractdeclarative.qdoc @@ -152,6 +152,19 @@ * Add a list of \l{Custom3DItem}s to the graph. Graph takes ownership of the added items. */ +/*! + * \qmlproperty bool AbstractGraph3D::polar + * \since QtDataVisualization 1.2 + * + * If \c {true}, the horizontal axes are changed into polar axes. The X axis becomes the + * angular axis and the Z axis becomes the radial axis. + * Polar mode is not available for bar graphs. + * + * Defaults to \c{false}. + * + * \sa orthoProjection, AbstractAxis3D::gridOffset, radialLabelOffset + */ + /*! * \qmlmethod void AbstractGraph3D::clearSelection() * Clears selection from all attached series. diff --git a/src/datavisualization/engine/abstract3dcontroller.cpp b/src/datavisualization/engine/abstract3dcontroller.cpp index 30434dca..e4fd0003 100644 --- a/src/datavisualization/engine/abstract3dcontroller.cpp +++ b/src/datavisualization/engine/abstract3dcontroller.cpp @@ -50,6 +50,7 @@ Abstract3DController::Abstract3DController(QRect initialViewport, Q3DScene *scen m_isCustomItemDirty(true), m_isSeriesVisualsDirty(true), m_renderPending(false), + m_isPolar(false), m_measureFps(false), m_numFrames(0), m_currentFps(0.0) @@ -176,6 +177,11 @@ void Abstract3DController::synchDataToRenderer() m_renderer->updateTheme(m_themeManager->activeTheme()); + if (m_changeTracker.polarChanged) { + m_renderer->updatePolar(m_isPolar); + m_changeTracker.polarChanged = false; + } + if (m_changeTracker.shadowQualityChanged) { m_renderer->updateShadowQuality(m_shadowQuality); m_changeTracker.shadowQualityChanged = false; @@ -1521,4 +1527,20 @@ float Abstract3DController::aspectRatio() return m_aspectRatio; } +void Abstract3DController::setPolar(bool enable) +{ + if (enable != m_isPolar) { + m_isPolar = enable; + m_changeTracker.polarChanged = true; + m_isDataDirty = true; + emit polarChanged(m_isPolar); + emitNeedRender(); + } +} + +bool Abstract3DController::isPolar() const +{ + return m_isPolar; +} + QT_END_NAMESPACE_DATAVISUALIZATION diff --git a/src/datavisualization/engine/abstract3dcontroller_p.h b/src/datavisualization/engine/abstract3dcontroller_p.h index 0e4d1add..7d721b0c 100644 --- a/src/datavisualization/engine/abstract3dcontroller_p.h +++ b/src/datavisualization/engine/abstract3dcontroller_p.h @@ -90,6 +90,7 @@ struct Abstract3DChangeBitField { bool axisXTitleFixedChanged : 1; bool axisYTitleFixedChanged : 1; bool axisZTitleFixedChanged : 1; + bool polarChanged : 1; Abstract3DChangeBitField() : themeChanged(true), @@ -133,7 +134,8 @@ struct Abstract3DChangeBitField { axisZTitleVisibilityChanged(true), axisXTitleFixedChanged(true), axisYTitleFixedChanged(true), - axisZTitleFixedChanged(true) + axisZTitleFixedChanged(true), + polarChanged(true) { } }; @@ -175,6 +177,7 @@ protected: bool m_isCustomItemDirty; bool m_isSeriesVisualsDirty; bool m_renderPending; + bool m_isPolar; QList m_seriesList; @@ -261,6 +264,17 @@ public: void setOrthoProjection(bool enable); bool isOrthoProjection() const; + void setMeasureFps(bool enable); + inline bool measureFps() const { return m_measureFps; } + inline qreal currentFps() const { return m_currentFps; } + + QAbstract3DGraph::ElementType selectedElement() const; + + void setAspectRatio(float ratio); + float aspectRatio(); + void setPolar(bool enable); + bool isPolar() const; + void emitNeedRender(); virtual void clearSelection() = 0; @@ -320,17 +334,8 @@ public slots: // Renderer callback handlers void handleRequestShadowQuality(QAbstract3DGraph::ShadowQuality quality); - void setMeasureFps(bool enable); - inline bool measureFps() const { return m_measureFps; } - inline qreal currentFps() const { return m_currentFps; } - - QAbstract3DGraph::ElementType selectedElement() const; - void updateCustomItem(); - void setAspectRatio(float ratio); - float aspectRatio(); - signals: void shadowQualityChanged(QAbstract3DGraph::ShadowQuality quality); void activeInputHandlerChanged(QAbstract3DInputHandler *inputHandler); @@ -346,6 +351,7 @@ signals: void orthoProjectionChanged(bool enabled); void aspectRatioChanged(float ratio); void optimizationHintsChanged(QAbstract3DGraph::OptimizationHints hints); + void polarChanged(bool enabled); protected: virtual QAbstract3DAxis *createDefaultAxis(QAbstract3DAxis::AxisOrientation orientation); diff --git a/src/datavisualization/engine/abstract3drenderer.cpp b/src/datavisualization/engine/abstract3drenderer.cpp index 132a0514..56a5ba60 100644 --- a/src/datavisualization/engine/abstract3drenderer.cpp +++ b/src/datavisualization/engine/abstract3drenderer.cpp @@ -25,8 +25,16 @@ #include "qcustom3ditem_p.h" #include "qcustom3dlabel_p.h" +#include + QT_BEGIN_NAMESPACE_DATAVISUALIZATION +const qreal doublePi(M_PI * 2.0); +const int polarGridRoundness(64); +const qreal polarGridAngle(doublePi / qreal(polarGridRoundness)); +const float polarGridAngleDegrees(float(360.0 / qreal(polarGridRoundness))); +const qreal polarGridHalfAngle(polarGridAngle / 2.0); + Abstract3DRenderer::Abstract3DRenderer(Abstract3DController *controller) : QObject(0), m_hasNegativeValues(false), @@ -37,6 +45,7 @@ Abstract3DRenderer::Abstract3DRenderer(Abstract3DController *controller) m_cachedSelectionMode(QAbstract3DGraph::SelectionNone), m_cachedOptimizationHint(QAbstract3DGraph::OptimizationDefault), m_textureHelper(0), + m_depthTexture(0), m_cachedScene(new Q3DScene()), m_selectionDirty(true), m_selectionState(SelectNone), @@ -55,7 +64,15 @@ Abstract3DRenderer::Abstract3DRenderer(Abstract3DController *controller) m_backgroundObj(0), m_gridLineObj(0), m_labelObj(0), - m_graphAspectRatio(2.0f) + m_graphAspectRatio(2.0f), + m_polarGraph(false), + m_xRightAngleRotation(QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, 90.0f)), + m_yRightAngleRotation(QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, 90.0f)), + m_zRightAngleRotation(QQuaternion::fromAxisAndAngle(0.0f, 0.0f, 1.0f, 90.0f)), + m_xRightAngleRotationNeg(QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, -90.0f)), + m_yRightAngleRotationNeg(QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, -90.0f)), + m_zRightAngleRotationNeg(QQuaternion::fromAxisAndAngle(0.0f, 0.0f, 1.0f, -90.0f)), + m_xFlipRotation(QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, -180.0f)) { QObject::connect(m_drawer, &Drawer::drawerChanged, this, &Abstract3DRenderer::updateTextures); QObject::connect(this, &Abstract3DRenderer::needRender, controller, @@ -89,7 +106,10 @@ Abstract3DRenderer::~Abstract3DRenderer() ObjectHelper::releaseObjectHelper(this, m_gridLineObj); ObjectHelper::releaseObjectHelper(this, m_labelObj); - delete m_textureHelper; + if (m_textureHelper) { + m_textureHelper->deleteTexture(&m_depthTexture); + delete m_textureHelper; + } } void Abstract3DRenderer::initializeOpenGL() @@ -287,6 +307,14 @@ void Abstract3DRenderer::updateAspectRatio(float ratio) updateCustomItemPositions(); } +void Abstract3DRenderer::updatePolar(bool enable) +{ + m_polarGraph = enable; + foreach (SeriesRenderCache *cache, m_renderCacheList) + cache->setDataDirty(true); + updateCustomItemPositions(); +} + void Abstract3DRenderer::updateOptimizationHint(QAbstract3DGraph::OptimizationHints hint) { m_cachedOptimizationHint = hint; @@ -607,10 +635,9 @@ void Abstract3DRenderer::drawAxisTitleY(const QVector3D &sideLabelRotation, QQuaternion titleRotation; if (m_axisCacheY.isTitleFixed()) { titleRotation = QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, yRotation) - * QQuaternion::fromAxisAndAngle(0.0f, 0.0f, 1.0f, 90.0f); + * m_zRightAngleRotation; } else { - titleRotation = totalRotation - * QQuaternion::fromAxisAndAngle(0.0f, 0.0f, 1.0f, 90.0f); + titleRotation = totalRotation * m_zRightAngleRotation; } dummyItem.setTranslation(titleTrans + titleOffsetVector); @@ -793,7 +820,6 @@ void Abstract3DRenderer::loadLabelMesh() QStringLiteral(":/defaultMeshes/plane")); } - void Abstract3DRenderer::generateBaseColorTexture(const QColor &color, GLuint *texture) { m_textureHelper->deleteTexture(texture); @@ -1131,4 +1157,118 @@ void Abstract3DRenderer::drawCustomItems(RenderingState state, } } +void Abstract3DRenderer::calculatePolarXZ(const QVector3D &dataPos, float &x, float &z) const +{ + // x is angular, z is radial + qreal angle = m_axisCacheX.formatter()->positionAt(dataPos.x()) * doublePi; + qreal radius = m_axisCacheZ.formatter()->positionAt(dataPos.z()); + + // Convert angle & radius to X and Z coords + x = float(radius * qSin(angle)) * m_graphAspectRatio; + z = -float(radius * qCos(angle)) * m_graphAspectRatio; +} + +void Abstract3DRenderer::drawRadialGrid(ShaderHelper *shader, float yFloorLinePos, + const QMatrix4x4 &projectionViewMatrix, + const QMatrix4x4 &depthMatrix) +{ + static QVector lineRotations; + if (!lineRotations.size()) { + lineRotations.resize(polarGridRoundness); + for (int j = 0; j < polarGridRoundness; j++) { + lineRotations[j] = QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, + polarGridAngleDegrees * float(j)); + } + } + int gridLineCount = m_axisCacheZ.gridLineCount(); + const QVector &gridPositions = m_axisCacheZ.formatter()->gridPositions(); + const QVector &subGridPositions = m_axisCacheZ.formatter()->subGridPositions(); + int mainSize = gridPositions.size(); + QVector3D translateVector(0.0f, yFloorLinePos, 0.0f); + for (int i = 0; i < gridLineCount; i++) { + float gridPosition = (i >= mainSize) + ? subGridPositions.at(i - mainSize) : gridPositions.at(i); + float radiusFraction = m_graphAspectRatio * gridPosition; + QVector3D gridLineScaler(radiusFraction * float(qSin(polarGridHalfAngle)), + gridLineWidth, gridLineWidth); + translateVector.setZ(gridPosition * m_graphAspectRatio); + for (int j = 0; j < polarGridRoundness; j++) { + QMatrix4x4 modelMatrix; + QMatrix4x4 itModelMatrix; + modelMatrix.rotate(lineRotations.at(j)); + itModelMatrix.rotate(lineRotations.at(j)); + modelMatrix.translate(translateVector); + modelMatrix.scale(gridLineScaler); + itModelMatrix.scale(gridLineScaler); + modelMatrix.rotate(m_xRightAngleRotationNeg); + itModelMatrix.rotate(m_xRightAngleRotationNeg); + QMatrix4x4 MVPMatrix = projectionViewMatrix * modelMatrix; + + shader->setUniformValue(shader->model(), modelMatrix); + shader->setUniformValue(shader->nModel(), itModelMatrix.inverted().transposed()); + shader->setUniformValue(shader->MVP(), MVPMatrix); +#if !defined(QT_OPENGL_ES_2) + if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { + // Set shadow shader bindings + QMatrix4x4 depthMVPMatrix = depthMatrix * modelMatrix; + shader->setUniformValue(shader->depth(), depthMVPMatrix); + // Draw the object + m_drawer->drawObject(shader, m_gridLineObj, 0, m_depthTexture); + } else { + // Draw the object + m_drawer->drawObject(shader, m_gridLineObj); + } +#else + m_drawer->drawLine(shader); +#endif + } + } +} + +void Abstract3DRenderer::drawAngularGrid(ShaderHelper *shader, float yFloorLinePos, + const QMatrix4x4 &projectionViewMatrix, + const QMatrix4x4 &depthMatrix) +{ + float halfRatio(m_graphAspectRatio / 2.0f); + QVector3D gridLineScaler(gridLineWidth, gridLineWidth, halfRatio); + int gridLineCount = m_axisCacheX.gridLineCount(); + const QVector &gridPositions = m_axisCacheX.formatter()->gridPositions(); + const QVector &subGridPositions = m_axisCacheX.formatter()->subGridPositions(); + int mainSize = gridPositions.size(); + QVector3D translateVector(0.0f, yFloorLinePos, -halfRatio); + for (int i = 0; i < gridLineCount; i++) { + QMatrix4x4 modelMatrix; + QMatrix4x4 itModelMatrix; + float gridPosition = (i >= mainSize) + ? subGridPositions.at(i - mainSize) : gridPositions.at(i); + QQuaternion lineRotation = QQuaternion::fromAxisAndAngle(upVector, gridPosition * 360.0f); + modelMatrix.rotate(lineRotation); + itModelMatrix.rotate(lineRotation); + modelMatrix.translate(translateVector); + modelMatrix.scale(gridLineScaler); + itModelMatrix.scale(gridLineScaler); + modelMatrix.rotate(m_xRightAngleRotationNeg); + itModelMatrix.rotate(m_xRightAngleRotationNeg); + QMatrix4x4 MVPMatrix = projectionViewMatrix * modelMatrix; + + shader->setUniformValue(shader->model(), modelMatrix); + shader->setUniformValue(shader->nModel(), itModelMatrix.inverted().transposed()); + shader->setUniformValue(shader->MVP(), MVPMatrix); +#if !defined(QT_OPENGL_ES_2) + if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { + // Set shadow shader bindings + QMatrix4x4 depthMVPMatrix = depthMatrix * modelMatrix; + shader->setUniformValue(shader->depth(), depthMVPMatrix); + // Draw the object + m_drawer->drawObject(shader, m_gridLineObj, 0, m_depthTexture); + } else { + // Draw the object + m_drawer->drawObject(shader, m_gridLineObj); + } +#else + m_drawer->drawLine(shader); +#endif + } +} + QT_END_NAMESPACE_DATAVISUALIZATION diff --git a/src/datavisualization/engine/abstract3drenderer_p.h b/src/datavisualization/engine/abstract3drenderer_p.h index 325ac8e6..33337441 100644 --- a/src/datavisualization/engine/abstract3drenderer_p.h +++ b/src/datavisualization/engine/abstract3drenderer_p.h @@ -125,6 +125,7 @@ public: virtual void updateCustomItem(CustomRenderItem *renderItem); virtual void updateAspectRatio(float ratio); + virtual void updatePolar(bool enable); virtual QVector3D convertPositionToTranslation(const QVector3D &position, bool isAbsolute) = 0; @@ -155,6 +156,7 @@ public: GLuint depthTexture, GLfloat shadowQuality); #endif QVector4D indexToSelectionColor(GLint index); + void calculatePolarXZ(const QVector3D &dataPos, float &x, float &z) const; signals: void needRender(); // Emit this if something in renderer causes need for another render pass. @@ -197,6 +199,11 @@ protected: void loadGridLineMesh(); void loadLabelMesh(); + void drawRadialGrid(ShaderHelper *shader, float yFloorLinePos, + const QMatrix4x4 &projectionViewMatrix, const QMatrix4x4 &depthMatrix); + void drawAngularGrid(ShaderHelper *shader, float yFloorLinePos, + const QMatrix4x4 &projectionViewMatrix, const QMatrix4x4 &depthMatrix); + bool m_hasNegativeValues; Q3DTheme *m_cachedTheme; Drawer *m_drawer; @@ -211,6 +218,7 @@ protected: AxisRenderCache m_axisCacheY; AxisRenderCache m_axisCacheZ; TextureHelper *m_textureHelper; + GLuint m_depthTexture; Q3DScene *m_cachedScene; bool m_selectionDirty; @@ -244,6 +252,15 @@ protected: ObjectHelper *m_labelObj; // Shared reference float m_graphAspectRatio; + bool m_polarGraph; + + QQuaternion m_xRightAngleRotation; + QQuaternion m_yRightAngleRotation; + QQuaternion m_zRightAngleRotation; + QQuaternion m_xRightAngleRotationNeg; + QQuaternion m_yRightAngleRotationNeg; + QQuaternion m_zRightAngleRotationNeg; + QQuaternion m_xFlipRotation; private: friend class Abstract3DController; diff --git a/src/datavisualization/engine/bars3drenderer.cpp b/src/datavisualization/engine/bars3drenderer.cpp index f909c7d6..70310c23 100644 --- a/src/datavisualization/engine/bars3drenderer.cpp +++ b/src/datavisualization/engine/bars3drenderer.cpp @@ -31,7 +31,6 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION -const GLfloat gridLineWidth = 0.005f; const bool sliceGridLabels = true; Bars3DRenderer::Bars3DRenderer(Bars3DController *controller) @@ -50,7 +49,6 @@ Bars3DRenderer::Bars3DRenderer(Bars3DController *controller) m_backgroundShader(0), m_labelShader(0), m_bgrTexture(0), - m_depthTexture(0), m_selectionTexture(0), m_depthFrameBuffer(0), m_selectionFrameBuffer(0), @@ -421,7 +419,6 @@ void Bars3DRenderer::drawSlicedScene() GLfloat barPosX = 0; QVector3D lightPos; QVector4D lightColor = Utils::vectorFromColor(m_cachedTheme->lightColor()); - static QQuaternion ninetyDegreeRotation = QQuaternion::fromAxisAndAngle(upVector, 90.0f); // Specify viewport glViewport(m_secondarySubViewport.x(), @@ -698,7 +695,7 @@ void Bars3DRenderer::drawSlicedScene() barPosX = item.translation().x(); } else { barPosX = -(item.translation().z()); // flip z; frontmost bar to the left - barRotation *= ninetyDegreeRotation; + barRotation *= m_yRightAngleRotation; } modelMatrix.translate(barPosX, barPosY, 0.0f); @@ -1701,9 +1698,9 @@ void Bars3DRenderer::drawBackground(GLfloat rowScaleFactor, GLfloat columnScaleF modelMatrix.scale(backgroundScaler); if (m_yFlipped) - modelMatrix.rotate(90.0f, 1.0f, 0.0f, 0.0f); + modelMatrix.rotate(m_xRightAngleRotation); else - modelMatrix.rotate(-90.0f, 1.0f, 0.0f, 0.0f); + modelMatrix.rotate(m_xRightAngleRotationNeg); itModelMatrix = modelMatrix; @@ -1835,9 +1832,9 @@ void Bars3DRenderer::drawGridLines(GLfloat rowScaleFactor, GLfloat columnScaleFa QVector3D gridLineScaler(rowScaleFactor, gridLineWidth, gridLineWidth); if (m_yFlipped) - lineRotation = QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, 90.0f); + lineRotation = m_xRightAngleRotation; else - lineRotation = QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, -90.0f); + lineRotation = m_xRightAngleRotationNeg; // Floor lines: rows for (GLfloat row = 0.0f; row <= m_cachedRowCount; row++) { @@ -1879,7 +1876,7 @@ void Bars3DRenderer::drawGridLines(GLfloat rowScaleFactor, GLfloat columnScaleFa // Floor lines: columns #if defined(QT_OPENGL_ES_2) - lineRotation = QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, 90.0f); + lineRotation = m_yRightAngleRotation; #endif gridLineScaler = QVector3D(gridLineWidth, gridLineWidth, columnScaleFactor); for (GLfloat bar = 0.0f; bar <= m_cachedColumnCount; bar++) { @@ -1939,8 +1936,8 @@ void Bars3DRenderer::drawGridLines(GLfloat rowScaleFactor, GLfloat columnScaleFa modelMatrix.scale(gridLineScaler); itModelMatrix.scale(gridLineScaler); if (m_zFlipped) { - modelMatrix.rotate(180.0f, 1.0f, 0.0f, 0.0f); - itModelMatrix.rotate(180.0f, 1.0f, 0.0f, 0.0f); + modelMatrix.rotate(m_xFlipRotation); + itModelMatrix.rotate(m_xFlipRotation); } MVPMatrix = projectionViewMatrix * modelMatrix; @@ -1973,9 +1970,9 @@ void Bars3DRenderer::drawGridLines(GLfloat rowScaleFactor, GLfloat columnScaleFa xWallLinePosition = -xWallLinePosition; if (m_xFlipped) - lineRotation = QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, -90.0f); + lineRotation = m_yRightAngleRotationNeg; else - lineRotation = QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, 90.0f); + lineRotation = m_yRightAngleRotation; gridLineScaler = QVector3D(gridLineWidth, gridLineWidth, columnScaleFactor); for (int line = 0; line < gridLineCount; line++) { diff --git a/src/datavisualization/engine/bars3drenderer_p.h b/src/datavisualization/engine/bars3drenderer_p.h index 29835741..466f7bed 100644 --- a/src/datavisualization/engine/bars3drenderer_p.h +++ b/src/datavisualization/engine/bars3drenderer_p.h @@ -68,7 +68,6 @@ private: ShaderHelper *m_backgroundShader; ShaderHelper *m_labelShader; GLuint m_bgrTexture; - GLuint m_depthTexture; GLuint m_selectionTexture; GLuint m_depthFrameBuffer; GLuint m_selectionFrameBuffer; diff --git a/src/datavisualization/engine/qabstract3dgraph.cpp b/src/datavisualization/engine/qabstract3dgraph.cpp index b8fa92e8..6240f714 100644 --- a/src/datavisualization/engine/qabstract3dgraph.cpp +++ b/src/datavisualization/engine/qabstract3dgraph.cpp @@ -591,8 +591,15 @@ qreal QAbstract3DGraph::currentFps() const * \property QAbstract3DGraph::orthoProjection * \since QtDataVisualization 1.1 * - * If \c {true}, orthographic projection will be used for displaying the graph. Defaults to \c{false}. + * If \c {true}, orthographic projection will be used for displaying the graph. + * \note Orthographic projection can be used to create 2D graphs by replacing the default input + * handler with one that doesn't allow rotating the graph and setting the camera to view the graph + * directly from the side or from the top. Also, axis labels typically need to be rotated when + * viewing the graph from the sides. + * Defaults to \c{false}. * \note Shadows will be disabled when set to \c{true}. + * + * \sa QAbstract3DAxis::labelAutoRotation, Q3DCamera::cameraPreset */ void QAbstract3DGraph::setOrthoProjection(bool enable) { @@ -645,6 +652,28 @@ QAbstract3DGraph::OptimizationHints QAbstract3DGraph::optimizationHints() const return d_ptr->m_visualController->optimizationHints(); } +/*! + * \property QAbstract3DGraph::polar + * \since QtDataVisualization 1.2 + * + * If \c {true}, the horizontal axes are changed into polar axes. The X axis becomes the + * angular axis and the Z axis becomes the radial axis. + * Polar mode is not available for bar graphs. + * + * Defaults to \c{false}. + * + * \sa orthoProjection, QAbstract3DAxis::gridOffset, radialLabelOffset + */ +void QAbstract3DGraph::setPolar(bool enable) +{ + d_ptr->m_visualController->setPolar(enable); +} + +bool QAbstract3DGraph::isPolar() const +{ + return d_ptr->m_visualController->isPolar(); +} + /*! * \internal */ @@ -795,6 +824,8 @@ void QAbstract3DGraphPrivate::setVisualController(Abstract3DController *controll QObject::connect(m_visualController, &Abstract3DController::aspectRatioChanged, q_ptr, &QAbstract3DGraph::aspectRatioChanged); + QObject::connect(m_visualController, &Abstract3DController::polarChanged, q_ptr, + &QAbstract3DGraph::polarChanged); } void QAbstract3DGraphPrivate::handleDevicePixelRatioChange() diff --git a/src/datavisualization/engine/qabstract3dgraph.h b/src/datavisualization/engine/qabstract3dgraph.h index 59f61aae..ca673358 100644 --- a/src/datavisualization/engine/qabstract3dgraph.h +++ b/src/datavisualization/engine/qabstract3dgraph.h @@ -50,6 +50,7 @@ class QT_DATAVISUALIZATION_EXPORT QAbstract3DGraph : public QWindow, protected Q Q_PROPERTY(ElementType selectedElement READ selectedElement NOTIFY selectedElementChanged) Q_PROPERTY(qreal aspectRatio READ aspectRatio WRITE setAspectRatio NOTIFY aspectRatioChanged) Q_PROPERTY(OptimizationHints optimizationHints READ optimizationHints WRITE setOptimizationHints NOTIFY optimizationHintsChanged) + Q_PROPERTY(bool polar READ isPolar WRITE setPolar NOTIFY polarChanged) protected: explicit QAbstract3DGraph(QAbstract3DGraphPrivate *d, const QSurfaceFormat *format, @@ -150,6 +151,9 @@ public: void setOptimizationHints(OptimizationHints hints); OptimizationHints optimizationHints() const; + void setPolar(bool enable); + bool isPolar() const; + protected: bool event(QEvent *event); void resizeEvent(QResizeEvent *event); @@ -173,6 +177,7 @@ signals: void orthoProjectionChanged(bool enabled); void aspectRatioChanged(qreal ratio); void optimizationHintsChanged(QAbstract3DGraph::OptimizationHints hints); + void polarChanged(bool enabled); private: Q_DISABLE_COPY(QAbstract3DGraph) diff --git a/src/datavisualization/engine/scatter3drenderer.cpp b/src/datavisualization/engine/scatter3drenderer.cpp index 7ac81552..f6b044f6 100644 --- a/src/datavisualization/engine/scatter3drenderer.cpp +++ b/src/datavisualization/engine/scatter3drenderer.cpp @@ -38,7 +38,6 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION const GLfloat defaultMinSize = 0.01f; const GLfloat defaultMaxSize = 0.1f; const GLfloat itemScaler = 3.0f; -const GLfloat gridLineWidth = 0.005f; Scatter3DRenderer::Scatter3DRenderer(Scatter3DController *controller) : Abstract3DRenderer(controller), @@ -54,7 +53,6 @@ Scatter3DRenderer::Scatter3DRenderer(Scatter3DController *controller) m_backgroundShader(0), m_labelShader(0), m_bgrTexture(0), - m_depthTexture(0), m_selectionTexture(0), m_depthFrameBuffer(0), m_selectionFrameBuffer(0), @@ -1006,7 +1004,7 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) modelMatrix.scale(bgScale); // If we're viewing from below, background object must be flipped if (m_yFlipped) { - modelMatrix.rotate(180.0f, 1.0, 0.0, 0.0); + modelMatrix.rotate(m_xFlipRotation); modelMatrix.rotate(270.0f - backgroundRotation, 0.0f, 1.0f, 0.0f); } else { modelMatrix.rotate(backgroundRotation, 0.0f, 1.0f, 0.0f); @@ -1095,14 +1093,14 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) QQuaternion lineXRotation; if (m_xFlipped) - lineYRotation = QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, -90.0f); + lineYRotation = m_yRightAngleRotationNeg; else - lineYRotation = QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, 90.0f); + lineYRotation = m_yRightAngleRotation; if (m_yFlipped) - lineXRotation = QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, 90.0f); + lineXRotation = m_xRightAngleRotation; else - lineXRotation = QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, -90.0f); + lineXRotation = m_xRightAngleRotationNeg; GLfloat yFloorLinePosition = -1.0f - m_backgroundMargin + gridLineOffset; if (m_yFlipped) @@ -1189,8 +1187,8 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) modelMatrix.rotate(lineYRotation); itModelMatrix.rotate(lineYRotation); #else - modelMatrix.rotate(90.0f, 0.0f, 0.0f, 1.0f); - itModelMatrix.rotate(90.0f, 0.0f, 0.0f, 1.0f); + modelMatrix.rotate(m_zRightAngleRotation); + itModelMatrix.rotate(m_zRightAngleRotation); #endif MVPMatrix = projectionViewMatrix * modelMatrix; @@ -1221,7 +1219,7 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) // Columns (= X) if (m_axisCacheX.segmentCount() > 0) { #if defined(QT_OPENGL_ES_2) - lineXRotation = QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, 90.0f); + lineXRotation = m_yRightAngleRotation; #endif // Floor lines int gridLineCount = m_axisCacheX.gridLineCount(); @@ -1301,12 +1299,12 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) #if !defined(QT_OPENGL_ES_2) if (m_zFlipped) { - modelMatrix.rotate(180.0f, 1.0f, 0.0f, 0.0f); - itModelMatrix.rotate(180.0f, 1.0f, 0.0f, 0.0f); + modelMatrix.rotate(m_xFlipRotation); + itModelMatrix.rotate(m_xFlipRotation); } #else - modelMatrix.rotate(90.0f, 0.0f, 0.0f, 1.0f); - itModelMatrix.rotate(90.0f, 0.0f, 0.0f, 1.0f); + modelMatrix.rotate(m_zRightAngleRotation); + itModelMatrix.rotate(m_zRightAngleRotation); #endif MVPMatrix = projectionViewMatrix * modelMatrix; @@ -1368,8 +1366,8 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) itModelMatrix.scale(gridLineScaler); if (m_zFlipped) { - modelMatrix.rotate(180.0f, 1.0f, 0.0f, 0.0f); - itModelMatrix.rotate(180.0f, 1.0f, 0.0f, 0.0f); + modelMatrix.rotate(m_xFlipRotation); + itModelMatrix.rotate(m_xFlipRotation); } MVPMatrix = projectionViewMatrix * modelMatrix; diff --git a/src/datavisualization/engine/scatter3drenderer_p.h b/src/datavisualization/engine/scatter3drenderer_p.h index 7f213179..b0b1e411 100644 --- a/src/datavisualization/engine/scatter3drenderer_p.h +++ b/src/datavisualization/engine/scatter3drenderer_p.h @@ -61,7 +61,6 @@ private: ShaderHelper *m_backgroundShader; ShaderHelper *m_labelShader; GLuint m_bgrTexture; - GLuint m_depthTexture; GLuint m_selectionTexture; GLuint m_depthFrameBuffer; GLuint m_selectionFrameBuffer; diff --git a/src/datavisualization/engine/surface3drenderer.cpp b/src/datavisualization/engine/surface3drenderer.cpp index 60e57529..60e501f1 100644 --- a/src/datavisualization/engine/surface3drenderer.cpp +++ b/src/datavisualization/engine/surface3drenderer.cpp @@ -33,7 +33,6 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION // Margin for background (1.10 make it 10% larger to avoid // selection ball being drawn inside background) const GLfloat backgroundMargin = 1.1f; -const GLfloat gridLineWidth = 0.005f; const GLfloat sliceZScale = 0.1f; const GLfloat sliceUnits = 2.5f; const uint greenMultiplier = 256; @@ -53,12 +52,10 @@ Surface3DRenderer::Surface3DRenderer(Surface3DController *controller) m_selectionShader(0), m_labelShader(0), m_heightNormalizer(0.0f), - m_scaleFactor(0.0f), m_scaleX(0.0f), m_scaleZ(0.0f), m_scaleXWithBackground(0.0f), m_scaleZWithBackground(0.0f), - m_depthTexture(0), m_depthModelTexture(0), m_depthFrameBuffer(0), m_selectionFrameBuffer(0), @@ -301,10 +298,13 @@ void Surface3DRenderer::updateRows(const QVector srcArray->at(row)->at(j + sampleSpace.x()); } - if (cache->isFlatShadingEnabled()) - cache->surfaceObject()->updateCoarseRow(dstArray, row - sampleSpace.y()); - else - cache->surfaceObject()->updateSmoothRow(dstArray, row - sampleSpace.y()); + if (cache->isFlatShadingEnabled()) { + cache->surfaceObject()->updateCoarseRow(dstArray, row - sampleSpace.y(), + m_polarGraph); + } else { + cache->surfaceObject()->updateSmoothRow(dstArray, row - sampleSpace.y(), + m_polarGraph); + } } if (updateBuffers) cache->surfaceObject()->uploadBuffers(); @@ -343,9 +343,9 @@ void Surface3DRenderer::updateItems(const QVectorat(point.x())->at(point.y()); if (cache->isFlatShadingEnabled()) - cache->surfaceObject()->updateCoarseItem(dstArray, y, x); + cache->surfaceObject()->updateCoarseItem(dstArray, y, x, m_polarGraph); else - cache->surfaceObject()->updateSmoothItem(dstArray, y, x); + cache->surfaceObject()->updateSmoothItem(dstArray, y, x, m_polarGraph); } if (updateBuffers) cache->surfaceObject()->uploadBuffers(); @@ -538,10 +538,12 @@ void Surface3DRenderer::updateSliceObject(SurfaceSeriesRenderCache *cache, const QRect sliceRect(0, 0, sliceRow->size(), 2); if (sliceRow->size() > 0) { - if (cache->isFlatShadingEnabled()) - cache->sliceSurfaceObject()->setUpData(sliceDataArray, sliceRect, true, flipZX); - else - cache->sliceSurfaceObject()->setUpSmoothData(sliceDataArray, sliceRect, true, flipZX); + if (cache->isFlatShadingEnabled()) { + cache->sliceSurfaceObject()->setUpData(sliceDataArray, sliceRect, true, false, flipZX); + } else { + cache->sliceSurfaceObject()->setUpSmoothData(sliceDataArray, sliceRect, true, false, + flipZX); + } } } @@ -902,8 +904,8 @@ void Surface3DRenderer::drawSlicedScene() modelMatrix.scale(gridLineScaleY); itModelMatrix.scale(gridLineScaleY); #if (defined QT_OPENGL_ES_2) - modelMatrix.rotate(90.0f, 0.0f, 0.0f, 1.0f); - itModelMatrix.rotate(90.0f, 0.0f, 0.0f, 1.0f); + modelMatrix.rotate(m_zRightAngleRotation); + itModelMatrix.rotate(m_zRightAngleRotation); #endif MVPMatrix = projectionViewMatrix * modelMatrix; @@ -1365,7 +1367,7 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) // If we're viewing from below, background object must be flipped if (m_yFlipped) { - modelMatrix.rotate(180.0f, 1.0f, 0.0f, 0.0f); + modelMatrix.rotate(m_xFlipRotation); modelMatrix.rotate(270.0f - backgroundRotation, 0.0f, 1.0f, 0.0f); } else { modelMatrix.rotate(backgroundRotation, 0.0f, 1.0f, 0.0f); @@ -1461,205 +1463,213 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) QQuaternion lineXRotation; if (m_xFlipped) - lineYRotation = QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, -90.0f); + lineYRotation = m_yRightAngleRotationNeg; else - lineYRotation = QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, 90.0f); + lineYRotation = m_yRightAngleRotation; if (m_yFlipped) - lineXRotation = QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, 90.0f); + lineXRotation = m_xRightAngleRotation; else - lineXRotation = QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, -90.0f); + lineXRotation = m_xRightAngleRotationNeg; - GLfloat yFloorLinePosition = -backgroundMargin + gridLineOffset; + float yFloorLinePosition = -backgroundMargin + gridLineOffset; if (m_yFlipped) yFloorLinePosition = -yFloorLinePosition; // Rows (= Z) if (m_axisCacheZ.segmentCount() > 0) { - // Floor lines int gridLineCount = m_axisCacheZ.gridLineCount(); + // Floor lines + if (m_polarGraph) { + drawRadialGrid(lineShader, yFloorLinePosition, projectionViewMatrix, + depthProjectionViewMatrix); + } else { + for (int line = 0; line < gridLineCount; line++) { + QMatrix4x4 modelMatrix; + QMatrix4x4 MVPMatrix; + QMatrix4x4 itModelMatrix; - for (int line = 0; line < gridLineCount; line++) { - QMatrix4x4 modelMatrix; - QMatrix4x4 MVPMatrix; - QMatrix4x4 itModelMatrix; - - modelMatrix.translate(0.0f, yFloorLinePosition, - m_axisCacheZ.gridLinePosition(line)); + modelMatrix.translate(0.0f, yFloorLinePosition, + m_axisCacheZ.gridLinePosition(line)); - modelMatrix.scale(gridLineScaleX); - itModelMatrix.scale(gridLineScaleX); + modelMatrix.scale(gridLineScaleX); + itModelMatrix.scale(gridLineScaleX); - modelMatrix.rotate(lineXRotation); - itModelMatrix.rotate(lineXRotation); + modelMatrix.rotate(lineXRotation); + itModelMatrix.rotate(lineXRotation); - MVPMatrix = projectionViewMatrix * modelMatrix; + MVPMatrix = projectionViewMatrix * modelMatrix; - // Set the rest of the shader bindings - lineShader->setUniformValue(lineShader->model(), modelMatrix); - lineShader->setUniformValue(lineShader->nModel(), - itModelMatrix.inverted().transposed()); - lineShader->setUniformValue(lineShader->MVP(), MVPMatrix); + // Set the rest of the shader bindings + lineShader->setUniformValue(lineShader->model(), modelMatrix); + lineShader->setUniformValue(lineShader->nModel(), + itModelMatrix.inverted().transposed()); + lineShader->setUniformValue(lineShader->MVP(), MVPMatrix); #if !defined(QT_OPENGL_ES_2) - if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { - // Set shadow shader bindings - QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix; - lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix); - // Draw the object - m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture); - } else { - // Draw the object - m_drawer->drawObject(lineShader, m_gridLineObj); - } + if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { + // Set shadow shader bindings + QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix; + lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix); + // Draw the object + m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture); + } else { + // Draw the object + m_drawer->drawObject(lineShader, m_gridLineObj); + } #else - m_drawer->drawLine(lineShader); + m_drawer->drawLine(lineShader); #endif - } - - // Side wall lines - GLfloat lineXTrans = m_scaleXWithBackground - gridLineOffset; + } + // Side wall lines + GLfloat lineXTrans = m_scaleXWithBackground - gridLineOffset; - if (!m_xFlipped) - lineXTrans = -lineXTrans; + if (!m_xFlipped) + lineXTrans = -lineXTrans; - for (int line = 0; line < gridLineCount; line++) { - QMatrix4x4 modelMatrix; - QMatrix4x4 MVPMatrix; - QMatrix4x4 itModelMatrix; + for (int line = 0; line < gridLineCount; line++) { + QMatrix4x4 modelMatrix; + QMatrix4x4 MVPMatrix; + QMatrix4x4 itModelMatrix; - modelMatrix.translate(lineXTrans, 0.0f, m_axisCacheZ.gridLinePosition(line)); + modelMatrix.translate(lineXTrans, 0.0f, m_axisCacheZ.gridLinePosition(line)); - modelMatrix.scale(gridLineScaleY); - itModelMatrix.scale(gridLineScaleY); + modelMatrix.scale(gridLineScaleY); + itModelMatrix.scale(gridLineScaleY); #if !defined(QT_OPENGL_ES_2) - modelMatrix.rotate(lineYRotation); - itModelMatrix.rotate(lineYRotation); + modelMatrix.rotate(lineYRotation); + itModelMatrix.rotate(lineYRotation); #else - modelMatrix.rotate(90.0f, 0.0f, 0.0f, 1.0f); - itModelMatrix.rotate(90.0f, 0.0f, 0.0f, 1.0f); + modelMatrix.rotate(m_zRightAngleRotation); + itModelMatrix.rotate(m_zRightAngleRotation); #endif - MVPMatrix = projectionViewMatrix * modelMatrix; + MVPMatrix = projectionViewMatrix * modelMatrix; - // Set the rest of the shader bindings - lineShader->setUniformValue(lineShader->model(), modelMatrix); - lineShader->setUniformValue(lineShader->nModel(), - itModelMatrix.inverted().transposed()); - lineShader->setUniformValue(lineShader->MVP(), MVPMatrix); + // Set the rest of the shader bindings + lineShader->setUniformValue(lineShader->model(), modelMatrix); + lineShader->setUniformValue(lineShader->nModel(), + itModelMatrix.inverted().transposed()); + lineShader->setUniformValue(lineShader->MVP(), MVPMatrix); #if !defined(QT_OPENGL_ES_2) - if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { - // Set shadow shader bindings - QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix; - lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix); - // Draw the object - m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture); - } else { - // Draw the object - m_drawer->drawObject(lineShader, m_gridLineObj); - } + if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { + // Set shadow shader bindings + QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix; + lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix); + // Draw the object + m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture); + } else { + // Draw the object + m_drawer->drawObject(lineShader, m_gridLineObj); + } #else - m_drawer->drawLine(lineShader); + m_drawer->drawLine(lineShader); #endif + } } } // Columns (= X) if (m_axisCacheX.segmentCount() > 0) { #if defined(QT_OPENGL_ES_2) - lineXRotation = QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, 90.0f); + lineXRotation = m_yRightAngleRotation; #endif // Floor lines int gridLineCount = m_axisCacheX.gridLineCount(); - for (int line = 0; line < gridLineCount; line++) { - QMatrix4x4 modelMatrix; - QMatrix4x4 MVPMatrix; - QMatrix4x4 itModelMatrix; + if (m_polarGraph) { + drawAngularGrid(lineShader, yFloorLinePosition, projectionViewMatrix, + depthProjectionViewMatrix); + } else { + for (int line = 0; line < gridLineCount; line++) { + QMatrix4x4 modelMatrix; + QMatrix4x4 MVPMatrix; + QMatrix4x4 itModelMatrix; - modelMatrix.translate(m_axisCacheX.gridLinePosition(line), yFloorLinePosition, - 0.0f); + modelMatrix.translate(m_axisCacheX.gridLinePosition(line), yFloorLinePosition, + 0.0f); - modelMatrix.scale(gridLineScaleZ); - itModelMatrix.scale(gridLineScaleZ); + modelMatrix.scale(gridLineScaleZ); + itModelMatrix.scale(gridLineScaleZ); - modelMatrix.rotate(lineXRotation); - itModelMatrix.rotate(lineXRotation); + modelMatrix.rotate(lineXRotation); + itModelMatrix.rotate(lineXRotation); - MVPMatrix = projectionViewMatrix * modelMatrix; + MVPMatrix = projectionViewMatrix * modelMatrix; - // Set the rest of the shader bindings - lineShader->setUniformValue(lineShader->model(), modelMatrix); - lineShader->setUniformValue(lineShader->nModel(), - itModelMatrix.inverted().transposed()); - lineShader->setUniformValue(lineShader->MVP(), MVPMatrix); + // Set the rest of the shader bindings + lineShader->setUniformValue(lineShader->model(), modelMatrix); + lineShader->setUniformValue(lineShader->nModel(), + itModelMatrix.inverted().transposed()); + lineShader->setUniformValue(lineShader->MVP(), MVPMatrix); #if !defined(QT_OPENGL_ES_2) - if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { - // Set shadow shader bindings - QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix; - lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix); - // Draw the object - m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture); - } else { - // Draw the object - m_drawer->drawObject(lineShader, m_gridLineObj); - } + if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { + // Set shadow shader bindings + QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix; + lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix); + // Draw the object + m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture); + } else { + // Draw the object + m_drawer->drawObject(lineShader, m_gridLineObj); + } #else - m_drawer->drawLine(lineShader); + m_drawer->drawLine(lineShader); #endif - } + } - // Back wall lines - GLfloat lineZTrans = m_scaleZWithBackground - gridLineOffset; + // Back wall lines + GLfloat lineZTrans = m_scaleZWithBackground - gridLineOffset; - if (!m_zFlipped) - lineZTrans = -lineZTrans; + if (!m_zFlipped) + lineZTrans = -lineZTrans; - for (int line = 0; line < gridLineCount; line++) { - QMatrix4x4 modelMatrix; - QMatrix4x4 MVPMatrix; - QMatrix4x4 itModelMatrix; + for (int line = 0; line < gridLineCount; line++) { + QMatrix4x4 modelMatrix; + QMatrix4x4 MVPMatrix; + QMatrix4x4 itModelMatrix; - modelMatrix.translate(m_axisCacheX.gridLinePosition(line), 0.0f, lineZTrans); + modelMatrix.translate(m_axisCacheX.gridLinePosition(line), 0.0f, lineZTrans); - modelMatrix.scale(gridLineScaleY); - itModelMatrix.scale(gridLineScaleY); + modelMatrix.scale(gridLineScaleY); + itModelMatrix.scale(gridLineScaleY); #if !defined(QT_OPENGL_ES_2) - if (m_zFlipped) { - modelMatrix.rotate(180.0f, 1.0f, 0.0f, 0.0f); - itModelMatrix.rotate(180.0f, 1.0f, 0.0f, 0.0f); - } + if (m_zFlipped) { + modelMatrix.rotate(m_xFlipRotation); + itModelMatrix.rotate(m_xFlipRotation); + } #else - modelMatrix.rotate(90.0f, 0.0f, 0.0f, 1.0f); - itModelMatrix.rotate(90.0f, 0.0f, 0.0f, 1.0f); + modelMatrix.rotate(m_zRightAngleRotation); + itModelMatrix.rotate(m_zRightAngleRotation); #endif - MVPMatrix = projectionViewMatrix * modelMatrix; + MVPMatrix = projectionViewMatrix * modelMatrix; - // Set the rest of the shader bindings - lineShader->setUniformValue(lineShader->model(), modelMatrix); - lineShader->setUniformValue(lineShader->nModel(), - itModelMatrix.inverted().transposed()); - lineShader->setUniformValue(lineShader->MVP(), MVPMatrix); + // Set the rest of the shader bindings + lineShader->setUniformValue(lineShader->model(), modelMatrix); + lineShader->setUniformValue(lineShader->nModel(), + itModelMatrix.inverted().transposed()); + lineShader->setUniformValue(lineShader->MVP(), MVPMatrix); #if !defined(QT_OPENGL_ES_2) - if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { - // Set shadow shader bindings - QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix; - lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix); - // Draw the object - m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture); - } else { - // Draw the object - m_drawer->drawObject(lineShader, m_gridLineObj); - } + if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { + // Set shadow shader bindings + QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix; + lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix); + // Draw the object + m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture); + } else { + // Draw the object + m_drawer->drawObject(lineShader, m_gridLineObj); + } #else - m_drawer->drawLine(lineShader); + m_drawer->drawLine(lineShader); #endif + } } } @@ -1684,8 +1694,8 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) itModelMatrix.scale(gridLineScaleX); if (m_zFlipped) { - modelMatrix.rotate(180.0f, 1.0f, 0.0f, 0.0f); - itModelMatrix.rotate(180.0f, 1.0f, 0.0f, 0.0f); + modelMatrix.rotate(m_xFlipRotation); + itModelMatrix.rotate(m_xFlipRotation); } MVPMatrix = projectionViewMatrix * modelMatrix; @@ -1833,8 +1843,15 @@ void Surface3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa QVector3D positionZComp(0.0f, 0.0f, 0.0f); if (m_axisCacheZ.segmentCount() > 0) { int labelCount = m_axisCacheZ.labelCount(); - GLfloat labelXTrans = m_scaleXWithBackground + labelMargin; - GLfloat labelYTrans = -backgroundMargin; + float labelXTrans = 0.0f; + float labelYTrans = -backgroundMargin; + if (m_polarGraph) { + // TODO optional placement of radial labels - YTrans up only if over background + labelXTrans = m_scaleXWithBackground + labelMargin; + //labelYTrans += gridLineOffset + gridLineWidth; + } else { + labelXTrans = m_scaleXWithBackground + labelMargin; + } Qt::AlignmentFlag alignment = (m_xFlipped == m_zFlipped) ? Qt::AlignLeft : Qt::AlignRight; QVector3D labelRotation; if (m_xFlipped) @@ -1921,10 +1938,17 @@ void Surface3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa float offsetValue = 0.0f; for (int label = startIndex; label != endIndex; label = label + indexStep) { glPolygonOffset(offsetValue++ / -10.0f, 1.0f); + const LabelItem &axisLabelItem = *m_axisCacheZ.labelItems().at(label); // Draw the label here - labelTrans.setZ(m_axisCacheZ.labelPosition(label)); + if (m_polarGraph) { + float direction = m_zFlipped ? -1.0f : 1.0f; + labelTrans.setZ((m_axisCacheZ.formatter()->labelPositions().at(label) + * -m_graphAspectRatio + + m_drawer->scaledFontSize() + gridLineWidth) * direction); + } else { + labelTrans.setZ(m_axisCacheZ.labelPosition(label)); + } m_dummyRenderItem.setTranslation(labelTrans); - const LabelItem &axisLabelItem = *m_axisCacheZ.labelItems().at(label); if (drawSelection) { QVector4D labelColor = QVector4D(label / 255.0f, 0.0f, 0.0f, @@ -1953,8 +1977,13 @@ void Surface3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa fractionCamX = activeCamera->xRotation() * labelAngleFraction; int labelCount = m_axisCacheX.labelCount(); - GLfloat labelZTrans = m_scaleZWithBackground + labelMargin; + GLfloat labelZTrans = 0.0f; GLfloat labelYTrans = -backgroundMargin; + if (m_polarGraph) + labelYTrans += gridLineOffset + gridLineWidth; + else + labelZTrans = m_scaleZWithBackground + labelMargin; + Qt::AlignmentFlag alignment = (m_xFlipped != m_zFlipped) ? Qt::AlignLeft : Qt::AlignRight; QVector3D labelRotation; if (m_zFlipped) @@ -2023,7 +2052,14 @@ void Surface3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa } } } + QQuaternion totalRotation = Utils::calculateRotation(labelRotation); + if (m_polarGraph) { + if (m_zFlipped != m_xFlipped) + totalRotation *= m_zRightAngleRotation; + else + totalRotation *= m_zRightAngleRotationNeg; + } QVector3D labelTrans = QVector3D(0.0f, labelYTrans, @@ -2039,10 +2075,44 @@ void Surface3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa indexStep = 1; } float offsetValue = 0.0f; + bool showLastLabel = false; + QVector &gridPositions = m_axisCacheX.formatter()->gridPositions(); + int lastGridPosIndex = gridPositions.size() - 1; + if (gridPositions.size() + && (gridPositions.at(lastGridPosIndex) != 1.0f || gridPositions.at(0) != 0.0f)) { + // Avoid overlapping first and last label if they would get on same position + showLastLabel = true; + } + for (int label = startIndex; label != endIndex; label = label + indexStep) { glPolygonOffset(offsetValue++ / -10.0f, 1.0f); // Draw the label here - labelTrans.setX(m_axisCacheX.labelPosition(label)); + if (m_polarGraph) { + // Calculate angular position + if (label == lastGridPosIndex && !showLastLabel) + continue; + float gridPosition = m_axisCacheX.formatter()->gridPositions().at(label); + qreal angle = gridPosition * M_PI * 2.0; + labelTrans.setX((m_graphAspectRatio + labelMargin)* float(qSin(angle))); + labelTrans.setZ(-(m_graphAspectRatio + labelMargin) * float(qCos(angle))); + // Alignment depends on label angular position, as well as flips + Qt::AlignmentFlag vAlignment = Qt::AlignCenter; + Qt::AlignmentFlag hAlignment = Qt::AlignCenter; + const float centerMargin = 0.005f; + if (gridPosition < 0.25f - centerMargin || gridPosition > 0.75f + centerMargin) + vAlignment = m_zFlipped ? Qt::AlignTop : Qt::AlignBottom; + else if (gridPosition > 0.25f + centerMargin && gridPosition < 0.75f - centerMargin) + vAlignment = m_zFlipped ? Qt::AlignBottom : Qt::AlignTop; + + if (gridPosition < 0.50f - centerMargin && gridPosition > centerMargin) + hAlignment = m_zFlipped ? Qt::AlignRight : Qt::AlignLeft; + else if (gridPosition < 1.0f - centerMargin && gridPosition > 0.5f + centerMargin) + hAlignment = m_zFlipped ? Qt::AlignLeft : Qt::AlignRight; + + alignment = Qt::AlignmentFlag(vAlignment | hAlignment); + } else { + labelTrans.setX(m_axisCacheX.labelPosition(label)); + } m_dummyRenderItem.setTranslation(labelTrans); const LabelItem &axisLabelItem = *m_axisCacheX.labelItems().at(label); @@ -2298,13 +2368,18 @@ void Surface3DRenderer::calculateSceneScalingFactors() m_heightNormalizer = GLfloat(m_axisCacheY.max() - m_axisCacheY.min()); m_areaSize.setHeight(m_axisCacheZ.max() - m_axisCacheZ.min()); m_areaSize.setWidth(m_axisCacheX.max() - m_axisCacheX.min()); - m_scaleFactor = qMax(m_areaSize.width(), m_areaSize.height()); - m_scaleX = m_graphAspectRatio * m_areaSize.width() / m_scaleFactor; - m_scaleZ = m_graphAspectRatio * m_areaSize.height() / m_scaleFactor; + float scaleFactor = qMax(m_areaSize.width(), m_areaSize.height()); + if (m_polarGraph) { + m_scaleX = m_graphAspectRatio; + m_scaleZ = m_graphAspectRatio; + } else { + m_scaleX = m_graphAspectRatio * m_areaSize.width() / scaleFactor; + m_scaleZ = m_graphAspectRatio * m_areaSize.height() / scaleFactor; + } m_scaleXWithBackground = m_scaleX + backgroundMargin - 1.0f; m_scaleZWithBackground = m_scaleZ + backgroundMargin - 1.0f; - float factorScaler = 2.0f * m_graphAspectRatio / m_scaleFactor; + float factorScaler = 2.0f * m_graphAspectRatio / scaleFactor; m_axisCacheX.setScale(factorScaler * m_areaSize.width()); m_axisCacheZ.setScale(-factorScaler * m_areaSize.height()); m_axisCacheX.setTranslate(-m_axisCacheX.scale() / 2.0f); @@ -2327,10 +2402,12 @@ void Surface3DRenderer::updateObjects(SurfaceSeriesRenderCache *cache, bool dime QSurfaceDataArray &dataArray = cache->dataArray(); const QRect &sampleSpace = cache->sampleSpace(); - if (cache->isFlatShadingEnabled()) - cache->surfaceObject()->setUpData(dataArray, sampleSpace, dimensionChanged); - else - cache->surfaceObject()->setUpSmoothData(dataArray, sampleSpace, dimensionChanged); + if (cache->isFlatShadingEnabled()) { + cache->surfaceObject()->setUpData(dataArray, sampleSpace, dimensionChanged, m_polarGraph); + } else { + cache->surfaceObject()->setUpSmoothData(dataArray, sampleSpace, dimensionChanged, + m_polarGraph); + } } void Surface3DRenderer::updateSelectedPoint(const QPoint &position, QSurface3DSeries *series) diff --git a/src/datavisualization/engine/surface3drenderer_p.h b/src/datavisualization/engine/surface3drenderer_p.h index efa8ff7e..a621fae3 100644 --- a/src/datavisualization/engine/surface3drenderer_p.h +++ b/src/datavisualization/engine/surface3drenderer_p.h @@ -57,12 +57,10 @@ private: ShaderHelper *m_selectionShader; ShaderHelper *m_labelShader; GLfloat m_heightNormalizer; - GLfloat m_scaleFactor; GLfloat m_scaleX; GLfloat m_scaleZ; GLfloat m_scaleXWithBackground; GLfloat m_scaleZWithBackground; - GLuint m_depthTexture; GLuint m_depthModelTexture; GLuint m_depthFrameBuffer; GLuint m_selectionFrameBuffer; diff --git a/src/datavisualization/global/datavisualizationglobal_p.h b/src/datavisualization/global/datavisualizationglobal_p.h index abdac998..f1f51309 100644 --- a/src/datavisualization/global/datavisualizationglobal_p.h +++ b/src/datavisualization/global/datavisualizationglobal_p.h @@ -69,6 +69,7 @@ static const GLfloat gradientTextureWidth = 2.0f; static const GLfloat uniformTextureHeight = 64.0f; static const GLfloat uniformTextureWidth = 2.0f; static const GLfloat labelMargin = 0.05f; +static const GLfloat gridLineWidth = 0.005f; QT_END_NAMESPACE_DATAVISUALIZATION diff --git a/src/datavisualization/theme/q3dtheme.cpp b/src/datavisualization/theme/q3dtheme.cpp index 97ff8f81..70397e23 100644 --- a/src/datavisualization/theme/q3dtheme.cpp +++ b/src/datavisualization/theme/q3dtheme.cpp @@ -679,7 +679,7 @@ QLinearGradient Q3DTheme::multiHighlightGradient() const /*! * \property Q3DTheme::lightStrength * - * Specular light strength for the whole graph. Value must be 0.0f and 10.0f. + * Specular light strength for the whole graph. Value must be between 0.0f and 10.0f. */ void Q3DTheme::setLightStrength(float strength) { diff --git a/src/datavisualization/utils/surfaceobject.cpp b/src/datavisualization/utils/surfaceobject.cpp index d999ba90..f8675912 100644 --- a/src/datavisualization/utils/surfaceobject.cpp +++ b/src/datavisualization/utils/surfaceobject.cpp @@ -30,7 +30,8 @@ SurfaceObject::SurfaceObject(Surface3DRenderer *renderer) m_gridIndexCount(0), m_axisCacheX(renderer->m_axisCacheX), m_axisCacheY(renderer->m_axisCacheY), - m_axisCacheZ(renderer->m_axisCacheZ) + m_axisCacheZ(renderer->m_axisCacheZ), + m_renderer(renderer) { m_indicesType = GL_UNSIGNED_INT; @@ -49,7 +50,7 @@ SurfaceObject::~SurfaceObject() } void SurfaceObject::setUpSmoothData(const QSurfaceDataArray &dataArray, const QRect &space, - bool changeGeometry, bool flipXZ) + bool changeGeometry, bool polar, bool flipXZ) { m_columns = space.width(); m_rows = space.height(); @@ -75,9 +76,16 @@ void SurfaceObject::setUpSmoothData(const QSurfaceDataArray &dataArray, const QR const QSurfaceDataRow &p = *dataArray.at(i); for (int j = 0; j < m_columns; j++) { const QSurfaceDataItem &data = p.at(j); - float normalizedX = xCache.positionAt(data.x()); + float normalizedX; + float normalizedZ; + if (polar) { + // Slice don't use polar, so don't care about flip + m_renderer->calculatePolarXZ(data.position(), normalizedX, normalizedZ); + } else { + normalizedX = xCache.positionAt(data.x()); + normalizedZ = zCache.positionAt(data.z()); + } float normalizedY = m_axisCacheY.positionAt(data.y()); - float normalizedZ = zCache.positionAt(data.z()); m_vertices[totalIndex] = QVector3D(normalizedX, normalizedY, normalizedZ); if (changeGeometry) uvs[totalIndex] = QVector2D(GLfloat(j) * uvX, GLfloat(i) * uvY); @@ -137,7 +145,7 @@ void SurfaceObject::setUpSmoothData(const QSurfaceDataArray &dataArray, const QR createBuffers(m_vertices, uvs, m_normals, 0, changeGeometry); } -void SurfaceObject::updateSmoothRow(const QSurfaceDataArray &dataArray, int rowIndex) +void SurfaceObject::updateSmoothRow(const QSurfaceDataArray &dataArray, int rowIndex, bool polar) { // Update vertices int p = rowIndex * m_columns; @@ -145,9 +153,15 @@ void SurfaceObject::updateSmoothRow(const QSurfaceDataArray &dataArray, int rowI for (int j = 0; j < m_columns; j++) { const QSurfaceDataItem &data = dataRow.at(j); - float normalizedX = m_axisCacheX.positionAt(data.x()); + float normalizedX; + float normalizedZ; + if (polar) { + m_renderer->calculatePolarXZ(data.position(), normalizedX, normalizedZ); + } else { + normalizedX = m_axisCacheX.positionAt(data.x()); + normalizedZ = m_axisCacheZ.positionAt(data.z()); + } float normalizedY = m_axisCacheY.positionAt(data.y()); - float normalizedZ = m_axisCacheZ.positionAt(data.z()); m_vertices[p++] = QVector3D(normalizedX, normalizedY, normalizedZ); } @@ -196,13 +210,20 @@ void SurfaceObject::updateSmoothRow(const QSurfaceDataArray &dataArray, int rowI } } -void SurfaceObject::updateSmoothItem(const QSurfaceDataArray &dataArray, int row, int column) +void SurfaceObject::updateSmoothItem(const QSurfaceDataArray &dataArray, int row, int column, + bool polar) { // Update a vertice const QSurfaceDataItem &data = dataArray.at(row)->at(column); - float normalizedX = m_axisCacheX.positionAt(data.x()); + float normalizedX; + float normalizedZ; + if (polar) { + m_renderer->calculatePolarXZ(data.position(), normalizedX, normalizedZ); + } else { + normalizedX = m_axisCacheX.positionAt(data.x()); + normalizedZ = m_axisCacheZ.positionAt(data.z()); + } float normalizedY = m_axisCacheY.positionAt(data.y()); - float normalizedZ = m_axisCacheZ.positionAt(data.z()); m_vertices[row * m_columns + column] = QVector3D(normalizedX, normalizedY, normalizedZ); // Create normals @@ -331,7 +352,7 @@ void SurfaceObject::createSmoothGridlineIndices(int x, int y, int endX, int endY } void SurfaceObject::setUpData(const QSurfaceDataArray &dataArray, const QRect &space, - bool changeGeometry, bool flipXZ) + bool changeGeometry, bool polar, bool flipXZ) { m_columns = space.width(); m_rows = space.height(); @@ -362,9 +383,16 @@ void SurfaceObject::setUpData(const QSurfaceDataArray &dataArray, const QRect &s const QSurfaceDataRow &row = *dataArray.at(i); for (int j = 0; j < m_columns; j++) { const QSurfaceDataItem &data = row.at(j); - float normalizedX = xCache.positionAt(data.x()); + float normalizedX; + float normalizedZ; + if (polar) { + // Slice don't use polar, so don't care about flip + m_renderer->calculatePolarXZ(data.position(), normalizedX, normalizedZ); + } else { + normalizedX = xCache.positionAt(data.x()); + normalizedZ = zCache.positionAt(data.z()); + } float normalizedY = m_axisCacheY.positionAt(data.y()); - float normalizedZ = zCache.positionAt(data.z()); m_vertices[totalIndex] = QVector3D(normalizedX, normalizedY, normalizedZ); if (changeGeometry) uvs[totalIndex] = QVector2D(GLfloat(j) * uvX, GLfloat(i) * uvY); @@ -438,7 +466,7 @@ void SurfaceObject::setUpData(const QSurfaceDataArray &dataArray, const QRect &s delete[] indices; } -void SurfaceObject::updateCoarseRow(const QSurfaceDataArray &dataArray, int rowIndex) +void SurfaceObject::updateCoarseRow(const QSurfaceDataArray &dataArray, int rowIndex, bool polar) { int colLimit = m_columns - 1; int doubleColumns = m_columns * 2 - 2; @@ -448,9 +476,15 @@ void SurfaceObject::updateCoarseRow(const QSurfaceDataArray &dataArray, int rowI for (int j = 0; j < m_columns; j++) { const QSurfaceDataItem &data = dataRow.at(j); - float normalizedX = m_axisCacheX.positionAt(data.x()); + float normalizedX; + float normalizedZ; + if (polar) { + m_renderer->calculatePolarXZ(data.position(), normalizedX, normalizedZ); + } else { + normalizedX = m_axisCacheX.positionAt(data.x()); + normalizedZ = m_axisCacheZ.positionAt(data.z()); + } float normalizedY = m_axisCacheY.positionAt(data.y()); - float normalizedZ = m_axisCacheZ.positionAt(data.z()); m_vertices[p++] = QVector3D(normalizedX, normalizedY, normalizedZ); if (j > 0 && j < colLimit) { @@ -486,7 +520,8 @@ void SurfaceObject::updateCoarseRow(const QSurfaceDataArray &dataArray, int rowI } } -void SurfaceObject::updateCoarseItem(const QSurfaceDataArray &dataArray, int row, int column) +void SurfaceObject::updateCoarseItem(const QSurfaceDataArray &dataArray, int row, int column, + bool polar) { int colLimit = m_columns - 1; int doubleColumns = m_columns * 2 - 2; @@ -494,9 +529,15 @@ void SurfaceObject::updateCoarseItem(const QSurfaceDataArray &dataArray, int row // Update a vertice int p = row * doubleColumns + column * 2 - (column > 0); const QSurfaceDataItem &data = dataArray.at(row)->at(column); - float normalizedX = m_axisCacheX.positionAt(data.x()); + float normalizedX; + float normalizedZ; + if (polar) { + m_renderer->calculatePolarXZ(data.position(), normalizedX, normalizedZ); + } else { + normalizedX = m_axisCacheX.positionAt(data.x()); + normalizedZ = m_axisCacheZ.positionAt(data.z()); + } float normalizedY = m_axisCacheY.positionAt(data.y()); - float normalizedZ = m_axisCacheZ.positionAt(data.z()); m_vertices[p] = QVector3D(normalizedX, normalizedY, normalizedZ); p++; diff --git a/src/datavisualization/utils/surfaceobject_p.h b/src/datavisualization/utils/surfaceobject_p.h index 9c18dcb2..54e6dec3 100644 --- a/src/datavisualization/utils/surfaceobject_p.h +++ b/src/datavisualization/utils/surfaceobject_p.h @@ -54,13 +54,13 @@ public: ~SurfaceObject(); void setUpData(const QSurfaceDataArray &dataArray, const QRect &space, - bool changeGeometry, bool flipXZ = false); + bool changeGeometry, bool polar, bool flipXZ = false); void setUpSmoothData(const QSurfaceDataArray &dataArray, const QRect &space, - bool changeGeometry, bool flipXZ = false); - void updateCoarseRow(const QSurfaceDataArray &dataArray, int rowIndex); - void updateSmoothRow(const QSurfaceDataArray &dataArray, int startRow); - void updateSmoothItem(const QSurfaceDataArray &dataArray, int row, int column); - void updateCoarseItem(const QSurfaceDataArray &dataArray, int row, int column); + bool changeGeometry, bool polar, bool flipXZ = false); + void updateCoarseRow(const QSurfaceDataArray &dataArray, int rowIndex, bool polar); + void updateSmoothRow(const QSurfaceDataArray &dataArray, int startRow, bool polar); + void updateSmoothItem(const QSurfaceDataArray &dataArray, int row, int column, bool polar); + void updateCoarseItem(const QSurfaceDataArray &dataArray, int row, int column, bool polar); void createSmoothIndices(int x, int y, int endX, int endY); void createCoarseIndices(int x, int y, int columns, int rows); void createSmoothGridlineIndices(int x, int y, int endX, int endY); @@ -90,6 +90,7 @@ private: AxisRenderCache &m_axisCacheX; AxisRenderCache &m_axisCacheY; AxisRenderCache &m_axisCacheZ; + Surface3DRenderer *m_renderer; }; QT_END_NAMESPACE_DATAVISUALIZATION diff --git a/src/datavisualizationqml2/abstractdeclarative.cpp b/src/datavisualizationqml2/abstractdeclarative.cpp index f7ccbf21..30acc9e4 100644 --- a/src/datavisualizationqml2/abstractdeclarative.cpp +++ b/src/datavisualizationqml2/abstractdeclarative.cpp @@ -329,6 +329,8 @@ void AbstractDeclarative::setSharedController(Abstract3DController *controller) &AbstractDeclarative::aspectRatioChanged); QObject::connect(m_controller.data(), &Abstract3DController::optimizationHintsChanged, this, &AbstractDeclarative::handleOptimizationHintChange); + QObject::connect(m_controller.data(), &Abstract3DController::polarChanged, this, + &AbstractDeclarative::polarChanged); } void AbstractDeclarative::activateOpenGLContext(QQuickWindow *window) @@ -724,6 +726,16 @@ AbstractDeclarative::OptimizationHints AbstractDeclarative::optimizationHints() return OptimizationHints(intmode); } +void AbstractDeclarative::setPolar(bool enable) +{ + m_controller->setPolar(enable); +} + +bool AbstractDeclarative::isPolar() const +{ + return m_controller->isPolar(); +} + void AbstractDeclarative::windowDestroyed(QObject *obj) { // Remove destroyed window from window lists diff --git a/src/datavisualizationqml2/abstractdeclarative_p.h b/src/datavisualizationqml2/abstractdeclarative_p.h index dfcd9537..cd3d6134 100644 --- a/src/datavisualizationqml2/abstractdeclarative_p.h +++ b/src/datavisualizationqml2/abstractdeclarative_p.h @@ -72,6 +72,7 @@ class AbstractDeclarative : public QQuickItem Q_PROPERTY(ElementType selectedElement READ selectedElement NOTIFY selectedElementChanged REVISION 1) Q_PROPERTY(qreal aspectRatio READ aspectRatio WRITE setAspectRatio NOTIFY aspectRatioChanged REVISION 1) Q_PROPERTY(OptimizationHints optimizationHints READ optimizationHints WRITE setOptimizationHints NOTIFY optimizationHintsChanged REVISION 1) + Q_PROPERTY(bool polar READ isPolar WRITE setPolar NOTIFY polarChanged REVISION 2) public: enum SelectionFlag { @@ -193,6 +194,9 @@ public: void setOptimizationHints(OptimizationHints hints); OptimizationHints optimizationHints() const; + void setPolar(bool enable); + bool isPolar() const; + public slots: virtual void handleAxisXChanged(QAbstract3DAxis *axis) = 0; virtual void handleAxisYChanged(QAbstract3DAxis *axis) = 0; @@ -230,6 +234,7 @@ signals: Q_REVISION(1) void orthoProjectionChanged(bool enabled); Q_REVISION(1) void aspectRatioChanged(qreal ratio); Q_REVISION(1) void optimizationHintsChanged(AbstractDeclarative::OptimizationHints hints); + Q_REVISION(2) void polarChanged(bool enabled); private: QPointer m_controller; diff --git a/src/datavisualizationqml2/datavisualizationqml2_plugin.cpp b/src/datavisualizationqml2/datavisualizationqml2_plugin.cpp index 09780dc5..c4c0bc70 100644 --- a/src/datavisualizationqml2/datavisualizationqml2_plugin.cpp +++ b/src/datavisualizationqml2/datavisualizationqml2_plugin.cpp @@ -106,6 +106,12 @@ void QtDataVisualizationQml2Plugin::registerTypes(const char *uri) // New metatypes qRegisterMetaType("QAbstract3DGraph::ElementType"); + + // QtDataVisualization 1.2 + + // New revisions + qmlRegisterUncreatableType(uri, 1, 2, "AbstractGraph3D", + QLatin1String("Trying to create uncreatable: AbstractGraph3D.")); } QT_END_NAMESPACE_DATAVISUALIZATION diff --git a/tests/surfacetest/graphmodifier.cpp b/tests/surfacetest/graphmodifier.cpp index ed86f03c..56cd23cc 100644 --- a/tests/surfacetest/graphmodifier.cpp +++ b/tests/surfacetest/graphmodifier.cpp @@ -777,6 +777,11 @@ void GraphModifier::toggleZAscending(bool enabled) } } +void GraphModifier::togglePolar(bool enabled) +{ + m_graph->setPolar(enabled); +} + void GraphModifier::resetArrayAndSliders(QSurfaceDataArray *array, float minZ, float maxZ, float minX, float maxX) { m_axisMinSliderX->setValue(minX); diff --git a/tests/surfacetest/graphmodifier.h b/tests/surfacetest/graphmodifier.h index 5f1a252a..e70d35f2 100644 --- a/tests/surfacetest/graphmodifier.h +++ b/tests/surfacetest/graphmodifier.h @@ -131,6 +131,7 @@ public slots: void toggleAxisTitleFixed(bool enabled); void toggleXAscending(bool enabled); void toggleZAscending(bool enabled); + void togglePolar(bool enabled); private: void fillSeries(); diff --git a/tests/surfacetest/main.cpp b/tests/surfacetest/main.cpp index 5806d7b0..87f009cb 100644 --- a/tests/surfacetest/main.cpp +++ b/tests/surfacetest/main.cpp @@ -393,6 +393,10 @@ int main(int argc, char *argv[]) zAscendingCB->setText(QStringLiteral("Z Ascending")); zAscendingCB->setChecked(true); + QCheckBox *polarCB = new QCheckBox(widget); + polarCB->setText(QStringLiteral("Polar")); + polarCB->setChecked(false); + // Add controls to the layout #ifdef MULTI_SERIES vLayout->addWidget(series1CB); @@ -443,6 +447,7 @@ int main(int argc, char *argv[]) vLayout->addWidget(axisMinSliderZ); vLayout->addWidget(xAscendingCB); vLayout->addWidget(zAscendingCB); + vLayout->addWidget(polarCB); vLayout2->addWidget(new QLabel(QStringLiteral("Change font"))); vLayout2->addWidget(fontList); vLayout2->addWidget(labelButton); @@ -650,6 +655,8 @@ int main(int argc, char *argv[]) modifier, &GraphModifier::toggleXAscending); QObject::connect(zAscendingCB, &QCheckBox::stateChanged, modifier, &GraphModifier::toggleZAscending); + QObject::connect(polarCB, &QCheckBox::stateChanged, + modifier, &GraphModifier::togglePolar); QObject::connect(aspectRatioSlider, &QSlider::valueChanged, modifier, &GraphModifier::setAspectRatio); -- cgit v1.2.3 From 36417dd3660f75c34328c3420bdd512436da86ff Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Wed, 25 Jun 2014 13:00:55 +0300 Subject: Add flipHorizontalGrid property for surface This property allows drawind the horizontal grid and axis labels on top of the graph rather than on the bottom. This is useful when surface graph is used for 2D spectrograms in orthographic mode, as otherwise the grid is covered by the surface itself. Particularly relevant for polar plots of the same. Task-number: QTRD-3184 Change-Id: I9dbcdbfc754e13af52d2cf31a1d9991ef4b241f7 Reviewed-by: Mika Salmela --- .../qmlspectrogram/qml/qmlspectrogram/main.qml | 80 +++++++++++++--------- .../doc/src/qtdatavisualization-qml-surface3d.qdoc | 10 +++ .../engine/abstract3drenderer.cpp | 22 ++++-- .../engine/abstract3drenderer_p.h | 1 + src/datavisualization/engine/q3dsurface.cpp | 21 ++++++ src/datavisualization/engine/q3dsurface.h | 4 ++ src/datavisualization/engine/scatter3drenderer.cpp | 1 + .../engine/surface3dcontroller.cpp | 23 ++++++- .../engine/surface3dcontroller_p.h | 15 ++-- src/datavisualization/engine/surface3drenderer.cpp | 41 ++++++++--- src/datavisualization/engine/surface3drenderer_p.h | 2 + .../datavisualizationqml2_plugin.cpp | 1 + src/datavisualizationqml2/declarativesurface.cpp | 12 ++++ src/datavisualizationqml2/declarativesurface_p.h | 4 ++ 14 files changed, 182 insertions(+), 55 deletions(-) diff --git a/examples/datavisualization/qmlspectrogram/qml/qmlspectrogram/main.qml b/examples/datavisualization/qmlspectrogram/qml/qmlspectrogram/main.qml index 0b02c058..59da4993 100644 --- a/examples/datavisualization/qmlspectrogram/qml/qmlspectrogram/main.qml +++ b/examples/datavisualization/qmlspectrogram/qml/qmlspectrogram/main.qml @@ -52,6 +52,36 @@ Window { } //! [0] + ValueAxis3D { + id: xAxis + segmentCount: 8 + labelFormat: "%i" + title: "Angle" + titleVisible: true + labelAutoRotation: 30 + titleFixed: false + } + + ValueAxis3D { + id: yAxis + segmentCount: 8 + labelFormat: "%i \%" + title: "Value" + titleVisible: true + labelAutoRotation: 0 + titleFixed: false + } + + ValueAxis3D { + id: zAxis + segmentCount: 5 + labelFormat: "%i" + title: "Radius" + titleVisible: true + labelAutoRotation: 30 + titleFixed: false + } + Surface3D { id: surfaceGraph width: surfaceView.width @@ -60,18 +90,9 @@ Window { shadowQuality: AbstractGraph3D.ShadowQualityNone selectionMode: AbstractGraph3D.SelectionSlice | AbstractGraph3D.SelectionItemAndRow scene.activeCamera.cameraPreset: Camera3D.CameraPresetDirectlyAbove - axisX.segmentCount: 8 - axisX.subSegmentCount: 2 - axisX.labelFormat: "%i" - axisZ.segmentCount: 5 - axisZ.subSegmentCount: 2 - axisZ.labelFormat: "%i" - axisY.segmentCount: 8 - axisY.subSegmentCount: 1 - axisY.labelFormat: "%i \%" - axisY.title: "Value" - axisX.title: "Angle" - axisZ.title: "Radius" + axisX: xAxis + axisY: yAxis + axisZ: zAxis // Don't show specular spotlight as we don't want it to distort the colors theme.lightStrength: 0.0 @@ -79,6 +100,7 @@ Window { theme.backgroundEnabled: false orthoProjection: true + flipHorizontalGrid: true Surface3DSeries { id: surfaceSeries @@ -130,10 +152,16 @@ Window { onClicked: { if (surfaceGraph.orthoProjection === true) { surfaceGraph.orthoProjection = false; + xAxis.labelAutoRotation = 30 + yAxis.labelAutoRotation = 30 + zAxis.labelAutoRotation = 30 text = "Switch to orthogonal" } else { surfaceGraph.orthoProjection = true; surfaceGraph.scene.activeCamera.cameraPreset = Camera3D.CameraPresetDirectlyAbove + xAxis.labelAutoRotation = 0 + yAxis.labelAutoRotation = 0 + zAxis.labelAutoRotation = 0 text = "Switch to perspective" } } @@ -154,29 +182,17 @@ Window { } NewButton { - id: gridToggle + id: flipGridToggle Layout.fillWidth: true Layout.fillHeight: true - text: "Toggle axis grid" + text: "Toggle axis grid on top" onClicked: { - if (surfaceGraph.theme.gridEnabled === true) { - surfaceGraph.theme.gridEnabled = false; - } else { - surfaceGraph.theme.gridEnabled = true; - } - } - } - - NewButton { - id: gridOffsetToggle - Layout.fillWidth: true - Layout.fillHeight: true - text: "Toggle grid position" - onClicked: { - // TODO - if (surfaceGraph.theme.backgroundEnabled === true) { - } else { - surfaceGraph.theme.backgroundEnabled = true; + onClicked: { + if (surfaceGraph.flipHorizontalGrid === true) { + surfaceGraph.flipHorizontalGrid = false; + } else { + surfaceGraph.flipHorizontalGrid = true; + } } } } diff --git a/src/datavisualization/doc/src/qtdatavisualization-qml-surface3d.qdoc b/src/datavisualization/doc/src/qtdatavisualization-qml-surface3d.qdoc index 23a9a004..4c25cc0a 100644 --- a/src/datavisualization/doc/src/qtdatavisualization-qml-surface3d.qdoc +++ b/src/datavisualization/doc/src/qtdatavisualization-qml-surface3d.qdoc @@ -85,6 +85,16 @@ * To set the series, either use the addSeries() function or define them as children of the graph. */ +/*! + * \qmlproperty bool Surface3D::flipHorizontalGrid + * + * If \c{false}, the horizontal axis grid and labels are drawn on the horizontal background + * of the graph. + * If \c{true}, the horizontal axis grid and labels are drawn on the opposite side of the graph + * from the horizontal background. + * Defaults to \c{false}. + */ + /*! * \qmlmethod void Surface3D::addSeries(Surface3DSeries series) * Adds the \a series to the graph. diff --git a/src/datavisualization/engine/abstract3drenderer.cpp b/src/datavisualization/engine/abstract3drenderer.cpp index 56a5ba60..f7cb5499 100644 --- a/src/datavisualization/engine/abstract3drenderer.cpp +++ b/src/datavisualization/engine/abstract3drenderer.cpp @@ -61,6 +61,7 @@ Abstract3DRenderer::Abstract3DRenderer(Abstract3DController *controller) m_xFlipped(false), m_yFlipped(false), m_zFlipped(false), + m_yFlippedForGrid(false), m_backgroundObj(0), m_gridLineObj(0), m_labelObj(0), @@ -665,7 +666,7 @@ void Abstract3DRenderer::drawAxisTitleX(const QVector3D &labelRotation, float offsetRotation = labelRotation.z(); float extraRotation = -90.0f; Qt::AlignmentFlag alignment = Qt::AlignTop; - if (m_yFlipped) { + if (m_yFlippedForGrid) { alignment = Qt::AlignBottom; zRotation = 180.0f; if (m_zFlipped) { @@ -745,7 +746,7 @@ void Abstract3DRenderer::drawAxisTitleZ(const QVector3D &labelRotation, float xRotation = -90.0f; float extraRotation = 90.0f; Qt::AlignmentFlag alignment = Qt::AlignTop; - if (m_yFlipped) { + if (m_yFlippedForGrid) { alignment = Qt::AlignBottom; xRotation = -xRotation; if (m_zFlipped) { @@ -1185,6 +1186,10 @@ void Abstract3DRenderer::drawRadialGrid(ShaderHelper *shader, float yFloorLinePo const QVector &subGridPositions = m_axisCacheZ.formatter()->subGridPositions(); int mainSize = gridPositions.size(); QVector3D translateVector(0.0f, yFloorLinePos, 0.0f); + QQuaternion finalRotation = m_xRightAngleRotationNeg; + if (m_yFlippedForGrid) + finalRotation *= m_xFlipRotation; + for (int i = 0; i < gridLineCount; i++) { float gridPosition = (i >= mainSize) ? subGridPositions.at(i - mainSize) : gridPositions.at(i); @@ -1200,8 +1205,8 @@ void Abstract3DRenderer::drawRadialGrid(ShaderHelper *shader, float yFloorLinePo modelMatrix.translate(translateVector); modelMatrix.scale(gridLineScaler); itModelMatrix.scale(gridLineScaler); - modelMatrix.rotate(m_xRightAngleRotationNeg); - itModelMatrix.rotate(m_xRightAngleRotationNeg); + modelMatrix.rotate(finalRotation); + itModelMatrix.rotate(finalRotation); QMatrix4x4 MVPMatrix = projectionViewMatrix * modelMatrix; shader->setUniformValue(shader->model(), modelMatrix); @@ -1229,13 +1234,16 @@ void Abstract3DRenderer::drawAngularGrid(ShaderHelper *shader, float yFloorLineP const QMatrix4x4 &projectionViewMatrix, const QMatrix4x4 &depthMatrix) { - float halfRatio(m_graphAspectRatio / 2.0f); + float halfRatio((m_graphAspectRatio + (labelMargin / 2.0f)) / 2.0f); QVector3D gridLineScaler(gridLineWidth, gridLineWidth, halfRatio); int gridLineCount = m_axisCacheX.gridLineCount(); const QVector &gridPositions = m_axisCacheX.formatter()->gridPositions(); const QVector &subGridPositions = m_axisCacheX.formatter()->subGridPositions(); int mainSize = gridPositions.size(); QVector3D translateVector(0.0f, yFloorLinePos, -halfRatio); + QQuaternion finalRotation = m_xRightAngleRotationNeg; + if (m_yFlippedForGrid) + finalRotation *= m_xFlipRotation; for (int i = 0; i < gridLineCount; i++) { QMatrix4x4 modelMatrix; QMatrix4x4 itModelMatrix; @@ -1247,8 +1255,8 @@ void Abstract3DRenderer::drawAngularGrid(ShaderHelper *shader, float yFloorLineP modelMatrix.translate(translateVector); modelMatrix.scale(gridLineScaler); itModelMatrix.scale(gridLineScaler); - modelMatrix.rotate(m_xRightAngleRotationNeg); - itModelMatrix.rotate(m_xRightAngleRotationNeg); + modelMatrix.rotate(finalRotation); + itModelMatrix.rotate(finalRotation); QMatrix4x4 MVPMatrix = projectionViewMatrix * modelMatrix; shader->setUniformValue(shader->model(), modelMatrix); diff --git a/src/datavisualization/engine/abstract3drenderer_p.h b/src/datavisualization/engine/abstract3drenderer_p.h index 33337441..10e88f3f 100644 --- a/src/datavisualization/engine/abstract3drenderer_p.h +++ b/src/datavisualization/engine/abstract3drenderer_p.h @@ -246,6 +246,7 @@ protected: bool m_xFlipped; bool m_yFlipped; bool m_zFlipped; + bool m_yFlippedForGrid; ObjectHelper *m_backgroundObj; // Shared reference ObjectHelper *m_gridLineObj; // Shared reference diff --git a/src/datavisualization/engine/q3dsurface.cpp b/src/datavisualization/engine/q3dsurface.cpp index c5ce29d7..c9af656f 100644 --- a/src/datavisualization/engine/q3dsurface.cpp +++ b/src/datavisualization/engine/q3dsurface.cpp @@ -98,6 +98,8 @@ Q3DSurface::Q3DSurface(const QSurfaceFormat *format, QWindow *parent) dptr()->m_shared->initializeOpenGL(); QObject::connect(dptr()->m_shared, &Surface3DController::selectedSeriesChanged, this, &Q3DSurface::selectedSeriesChanged); + QObject::connect(dptr()->m_shared, &Surface3DController::flipHorizontalGridChanged, + this, &Q3DSurface::flipHorizontalGridChanged); } /*! @@ -229,6 +231,25 @@ QSurface3DSeries *Q3DSurface::selectedSeries() const return dptrc()->m_shared->selectedSeries(); } +/*! + * \property Q3DSurface::flipHorizontalGrid + * + * If \c{false}, the horizontal axis grid and labels are drawn on the horizontal background + * of the graph. + * If \c{true}, the horizontal axis grid and labels are drawn on the opposite side of the graph + * from the horizontal background. + * Defaults to \c{false}. + */ +void Q3DSurface::setFlipHorizontalGrid(bool flip) +{ + dptr()->m_shared->setFlipHorizontalGrid(flip); +} + +bool Q3DSurface::flipHorizontalGrid() const +{ + return dptrc()->m_shared->flipHorizontalGrid(); +} + /*! * Adds \a axis to the graph. The axes added via addAxis are not yet taken to use, * addAxis is simply used to give the ownership of the \a axis to the graph. diff --git a/src/datavisualization/engine/q3dsurface.h b/src/datavisualization/engine/q3dsurface.h index 9868c844..86740519 100644 --- a/src/datavisualization/engine/q3dsurface.h +++ b/src/datavisualization/engine/q3dsurface.h @@ -34,6 +34,7 @@ class QT_DATAVISUALIZATION_EXPORT Q3DSurface : public QAbstract3DGraph Q_PROPERTY(QValue3DAxis *axisY READ axisY WRITE setAxisY NOTIFY axisYChanged) Q_PROPERTY(QValue3DAxis *axisZ READ axisZ WRITE setAxisZ NOTIFY axisZChanged) Q_PROPERTY(QSurface3DSeries *selectedSeries READ selectedSeries NOTIFY selectedSeriesChanged) + Q_PROPERTY(bool flipHorizontalGrid READ flipHorizontalGrid WRITE setFlipHorizontalGrid NOTIFY flipHorizontalGridChanged) public: explicit Q3DSurface(const QSurfaceFormat *format = 0, QWindow *parent = 0); @@ -55,12 +56,15 @@ public: QList axes() const; QSurface3DSeries *selectedSeries() const; + void setFlipHorizontalGrid(bool flip); + bool flipHorizontalGrid() const; signals: void axisXChanged(QValue3DAxis *axis); void axisYChanged(QValue3DAxis *axis); void axisZChanged(QValue3DAxis *axis); void selectedSeriesChanged(QSurface3DSeries *series); + void flipHorizontalGridChanged(bool flip); private: Q3DSurfacePrivate *dptr(); diff --git a/src/datavisualization/engine/scatter3drenderer.cpp b/src/datavisualization/engine/scatter3drenderer.cpp index f6b044f6..944d9c9b 100644 --- a/src/datavisualization/engine/scatter3drenderer.cpp +++ b/src/datavisualization/engine/scatter3drenderer.cpp @@ -372,6 +372,7 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) m_yFlipped = true; else m_yFlipped = false; + m_yFlippedForGrid = m_yFlipped; // Polar axis grid drawing in abstract needs this // Calculate background rotation if (!m_zFlipped && !m_xFlipped) diff --git a/src/datavisualization/engine/surface3dcontroller.cpp b/src/datavisualization/engine/surface3dcontroller.cpp index c03bafd8..5293cb0c 100644 --- a/src/datavisualization/engine/surface3dcontroller.cpp +++ b/src/datavisualization/engine/surface3dcontroller.cpp @@ -29,7 +29,8 @@ Surface3DController::Surface3DController(QRect rect, Q3DScene *scene) m_renderer(0), m_selectedPoint(invalidSelectionPosition()), m_selectedSeries(0), - m_flatShadingSupported(true) + m_flatShadingSupported(true), + m_flipHorizontalGrid(false) { // Setting a null axis creates a new default axis according to orientation and graph type. // Note: these cannot be set in the Abstract3DController constructor, as they will call virtual @@ -79,6 +80,11 @@ void Surface3DController::synchDataToRenderer() m_renderer->updateSelectedPoint(m_selectedPoint, m_selectedSeries); m_changeTracker.selectedPointChanged = false; } + + if (m_changeTracker.flipHorizontalGridChanged) { + m_renderer->updateFlipHorizontalGrid(m_flipHorizontalGrid); + m_changeTracker.flipHorizontalGridChanged = false; + } } void Surface3DController::handleAxisAutoAdjustRangeChangedInOrientation( @@ -168,6 +174,21 @@ QList Surface3DController::surfaceSeriesList() return surfaceSeriesList; } +void Surface3DController::setFlipHorizontalGrid(bool flip) +{ + if (m_flipHorizontalGrid != flip) { + m_flipHorizontalGrid = flip; + m_changeTracker.flipHorizontalGridChanged = true; + emit flipHorizontalGridChanged(flip); + emitNeedRender(); + } +} + +bool Surface3DController::flipHorizontalGrid() const +{ + return m_flipHorizontalGrid; +} + void Surface3DController::setSelectionMode(QAbstract3DGraph::SelectionFlags mode) { // Currently surface only supports row and column modes when also slicing diff --git a/src/datavisualization/engine/surface3dcontroller_p.h b/src/datavisualization/engine/surface3dcontroller_p.h index 2be74f35..653f41c3 100644 --- a/src/datavisualization/engine/surface3dcontroller_p.h +++ b/src/datavisualization/engine/surface3dcontroller_p.h @@ -38,14 +38,16 @@ class Surface3DRenderer; class QSurface3DSeries; struct Surface3DChangeBitField { - bool selectedPointChanged : 1; - bool rowsChanged : 1; - bool itemChanged : 1; + bool selectedPointChanged : 1; + bool rowsChanged : 1; + bool itemChanged : 1; + bool flipHorizontalGridChanged : 1; Surface3DChangeBitField() : selectedPointChanged(true), rowsChanged(false), - itemChanged(false) + itemChanged(false), + flipHorizontalGridChanged(true) { } }; @@ -73,6 +75,7 @@ private: bool m_flatShadingSupported; QVector m_changedItems; QVector m_changedRows; + bool m_flipHorizontalGrid; public: explicit Surface3DController(QRect rect, Q3DScene *scene = 0); @@ -101,6 +104,9 @@ public: virtual void removeSeries(QAbstract3DSeries *series); virtual QList surfaceSeriesList(); + void setFlipHorizontalGrid(bool flip); + bool flipHorizontalGrid() const; + public slots: void handleArrayReset(); void handleRowsAdded(int startIndex, int count); @@ -113,6 +119,7 @@ public slots: signals: void selectedSeriesChanged(QSurface3DSeries *series); + void flipHorizontalGridChanged(bool flip); private: Q_DISABLE_COPY(Surface3DController) diff --git a/src/datavisualization/engine/surface3drenderer.cpp b/src/datavisualization/engine/surface3drenderer.cpp index 60e501f1..5fd59e5e 100644 --- a/src/datavisualization/engine/surface3drenderer.cpp +++ b/src/datavisualization/engine/surface3drenderer.cpp @@ -1050,6 +1050,17 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) else m_xFlipped = true; + if (m_flipHorizontalGrid) { + // Need to determine if camera is below graph top + float distanceToCenter = activeCamera->position().length() + / activeCamera->zoomLevel() / m_autoScaleAdjustment * 100.0f; + qreal cameraAngle = qreal(activeCamera->yRotation()) / 180.0 * M_PI; + float cameraYPos = float(qSin(cameraAngle)) * distanceToCenter; + m_yFlippedForGrid = cameraYPos < backgroundMargin; + } else { + m_yFlippedForGrid = m_yFlipped; + } + // calculate background rotation based on view matrix rotation if (viewMatrix.row(0).x() > 0 && viewMatrix.row(0).z() <= 0) backgroundRotation = 270.0f; @@ -1467,13 +1478,13 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) else lineYRotation = m_yRightAngleRotation; - if (m_yFlipped) + if (m_yFlippedForGrid) lineXRotation = m_xRightAngleRotation; else lineXRotation = m_xRightAngleRotationNeg; float yFloorLinePosition = -backgroundMargin + gridLineOffset; - if (m_yFlipped) + if (m_yFlipped != m_flipHorizontalGrid) yFloorLinePosition = -yFloorLinePosition; // Rows (= Z) @@ -1844,7 +1855,7 @@ void Surface3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa if (m_axisCacheZ.segmentCount() > 0) { int labelCount = m_axisCacheZ.labelCount(); float labelXTrans = 0.0f; - float labelYTrans = -backgroundMargin; + float labelYTrans = m_flipHorizontalGrid ? backgroundMargin : -backgroundMargin; if (m_polarGraph) { // TODO optional placement of radial labels - YTrans up only if over background labelXTrans = m_scaleXWithBackground + labelMargin; @@ -1861,7 +1872,7 @@ void Surface3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa if (labelAutoAngle == 0.0f) { if (m_zFlipped) labelRotation.setY(180.0f); - if (m_yFlipped) { + if (m_yFlippedForGrid) { if (m_zFlipped) labelRotation.setY(180.0f); else @@ -1873,7 +1884,7 @@ void Surface3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa } else { if (m_zFlipped) labelRotation.setY(180.0f); - if (m_yFlipped) { + if (m_yFlippedForGrid) { if (m_zFlipped) { if (m_xFlipped) { labelRotation.setX(90.0f - (labelAutoAngle - fractionCamX) @@ -1978,7 +1989,7 @@ void Surface3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa int labelCount = m_axisCacheX.labelCount(); GLfloat labelZTrans = 0.0f; - GLfloat labelYTrans = -backgroundMargin; + float labelYTrans = m_flipHorizontalGrid ? backgroundMargin : -backgroundMargin; if (m_polarGraph) labelYTrans += gridLineOffset + gridLineWidth; else @@ -1994,7 +2005,7 @@ void Surface3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa labelRotation = QVector3D(-90.0f, 90.0f, 0.0f); if (m_xFlipped) labelRotation.setY(-90.0f); - if (m_yFlipped) { + if (m_yFlippedForGrid) { if (m_xFlipped) labelRotation.setY(-90.0f); else @@ -2006,7 +2017,7 @@ void Surface3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa labelRotation.setY(-90.0f); else labelRotation.setY(90.0f); - if (m_yFlipped) { + if (m_yFlippedForGrid) { if (m_zFlipped) { if (m_xFlipped) { labelRotation.setX(90.0f - (2.0f * labelAutoAngle - fractionCamX) @@ -2055,10 +2066,12 @@ void Surface3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa QQuaternion totalRotation = Utils::calculateRotation(labelRotation); if (m_polarGraph) { - if (m_zFlipped != m_xFlipped) + if (!m_yFlippedForGrid && (m_zFlipped != m_xFlipped) + || m_yFlippedForGrid && (m_zFlipped == m_xFlipped)) { totalRotation *= m_zRightAngleRotation; - else + } else { totalRotation *= m_zRightAngleRotationNeg; + } } QVector3D labelTrans = QVector3D(0.0f, @@ -2108,7 +2121,8 @@ void Surface3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa hAlignment = m_zFlipped ? Qt::AlignRight : Qt::AlignLeft; else if (gridPosition < 1.0f - centerMargin && gridPosition > 0.5f + centerMargin) hAlignment = m_zFlipped ? Qt::AlignLeft : Qt::AlignRight; - + if (m_yFlippedForGrid && vAlignment != Qt::AlignCenter) + vAlignment = (vAlignment == Qt::AlignTop) ? Qt::AlignBottom : Qt::AlignTop; alignment = Qt::AlignmentFlag(vAlignment | hAlignment); } else { labelTrans.setX(m_axisCacheX.labelPosition(label)); @@ -2417,6 +2431,11 @@ void Surface3DRenderer::updateSelectedPoint(const QPoint &position, QSurface3DSe m_selectionDirty = true; } +void Surface3DRenderer::updateFlipHorizontalGrid(bool flip) +{ + m_flipHorizontalGrid = flip; +} + void Surface3DRenderer::resetClickedStatus() { m_clickedPosition = Surface3DController::invalidSelectionPosition(); diff --git a/src/datavisualization/engine/surface3drenderer_p.h b/src/datavisualization/engine/surface3drenderer_p.h index a621fae3..b1ebf33e 100644 --- a/src/datavisualization/engine/surface3drenderer_p.h +++ b/src/datavisualization/engine/surface3drenderer_p.h @@ -78,6 +78,7 @@ private: QPoint m_clickedPosition; bool m_selectionTexturesDirty; GLuint m_noShadowTexture; + bool m_flipHorizontalGrid; public: explicit Surface3DRenderer(Surface3DController *controller); @@ -93,6 +94,7 @@ public: void updateScene(Q3DScene *scene); void updateSlicingActive(bool isSlicing); void updateSelectedPoint(const QPoint &position, QSurface3DSeries *series); + void updateFlipHorizontalGrid(bool flip); inline QPoint clickedPosition() const { return m_clickedPosition; } void resetClickedStatus(); QVector3D convertPositionToTranslation(const QVector3D &position, bool isAbsolute); diff --git a/src/datavisualizationqml2/datavisualizationqml2_plugin.cpp b/src/datavisualizationqml2/datavisualizationqml2_plugin.cpp index c4c0bc70..6e6d9b1c 100644 --- a/src/datavisualizationqml2/datavisualizationqml2_plugin.cpp +++ b/src/datavisualizationqml2/datavisualizationqml2_plugin.cpp @@ -112,6 +112,7 @@ void QtDataVisualizationQml2Plugin::registerTypes(const char *uri) // New revisions qmlRegisterUncreatableType(uri, 1, 2, "AbstractGraph3D", QLatin1String("Trying to create uncreatable: AbstractGraph3D.")); + qmlRegisterType(uri, 1, 2, "Surface3D"); } QT_END_NAMESPACE_DATAVISUALIZATION diff --git a/src/datavisualizationqml2/declarativesurface.cpp b/src/datavisualizationqml2/declarativesurface.cpp index 3075d207..ec520459 100644 --- a/src/datavisualizationqml2/declarativesurface.cpp +++ b/src/datavisualizationqml2/declarativesurface.cpp @@ -32,6 +32,8 @@ DeclarativeSurface::DeclarativeSurface(QQuickItem *parent) QObject::connect(m_surfaceController, &Surface3DController::selectedSeriesChanged, this, &DeclarativeSurface::selectedSeriesChanged); + QObject::connect(m_surfaceController, &Surface3DController::flipHorizontalGridChanged, + this, &DeclarativeSurface::flipHorizontalGridChanged); } DeclarativeSurface::~DeclarativeSurface() @@ -74,6 +76,16 @@ QSurface3DSeries *DeclarativeSurface::selectedSeries() const return m_surfaceController->selectedSeries(); } +void DeclarativeSurface::setFlipHorizontalGrid(bool flip) +{ + m_surfaceController->setFlipHorizontalGrid(flip); +} + +bool DeclarativeSurface::flipHorizontalGrid() const +{ + return m_surfaceController->flipHorizontalGrid(); +} + QQmlListProperty DeclarativeSurface::seriesList() { return QQmlListProperty(this, this, diff --git a/src/datavisualizationqml2/declarativesurface_p.h b/src/datavisualizationqml2/declarativesurface_p.h index 6fe800ba..ff6e4d70 100644 --- a/src/datavisualizationqml2/declarativesurface_p.h +++ b/src/datavisualizationqml2/declarativesurface_p.h @@ -44,6 +44,7 @@ class DeclarativeSurface : public AbstractDeclarative Q_PROPERTY(QValue3DAxis *axisZ READ axisZ WRITE setAxisZ NOTIFY axisZChanged) Q_PROPERTY(QSurface3DSeries *selectedSeries READ selectedSeries NOTIFY selectedSeriesChanged) Q_PROPERTY(QQmlListProperty seriesList READ seriesList) + Q_PROPERTY(bool flipHorizontalGrid READ flipHorizontalGrid WRITE setFlipHorizontalGrid NOTIFY flipHorizontalGridChanged REVISION 1) Q_CLASSINFO("DefaultProperty", "seriesList") public: @@ -66,6 +67,8 @@ public: Q_INVOKABLE void removeSeries(QSurface3DSeries *series); QSurface3DSeries *selectedSeries() const; + void setFlipHorizontalGrid(bool flip); + bool flipHorizontalGrid() const; public slots: void handleAxisXChanged(QAbstract3DAxis *axis); @@ -77,6 +80,7 @@ signals: void axisYChanged(QValue3DAxis *axis); void axisZChanged(QValue3DAxis *axis); void selectedSeriesChanged(QSurface3DSeries *series); + Q_REVISION(1) void flipHorizontalGridChanged(bool flip); private: Surface3DController *m_surfaceController; -- cgit v1.2.3 From ffba0218893cd25b08718d4d6fd0c40c95aecf47 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Wed, 25 Jun 2014 14:45:01 +0300 Subject: Add radialLabelOffset property for graphs This property indicates where radial labels of the polar chart are drawn Task-number: QTRD-3184 Change-Id: I1500e67da5e578b90d679876130c2d56c1d08039 Reviewed-by: Mika Salmela --- .../qmlspectrogram/qml/qmlspectrogram/main.qml | 17 +++++++------- ...tdatavisualization-qml-abstractdeclarative.qdoc | 15 ++++++++++++- .../doc/src/qtdatavisualization-qml-surface3d.qdoc | 1 + .../engine/abstract3dcontroller.cpp | 21 +++++++++++++++++ .../engine/abstract3dcontroller_p.h | 8 ++++++- .../engine/abstract3drenderer.cpp | 6 +++++ .../engine/abstract3drenderer_p.h | 2 ++ src/datavisualization/engine/q3dsurface.cpp | 1 + src/datavisualization/engine/qabstract3dgraph.cpp | 26 +++++++++++++++++++++- src/datavisualization/engine/qabstract3dgraph.h | 5 +++++ src/datavisualization/engine/surface3drenderer.cpp | 23 ++++++++++++------- src/datavisualizationqml2/abstractdeclarative.cpp | 12 ++++++++++ src/datavisualizationqml2/abstractdeclarative_p.h | 5 +++++ 13 files changed, 122 insertions(+), 20 deletions(-) diff --git a/examples/datavisualization/qmlspectrogram/qml/qmlspectrogram/main.qml b/examples/datavisualization/qmlspectrogram/qml/qmlspectrogram/main.qml index 59da4993..a1321c14 100644 --- a/examples/datavisualization/qmlspectrogram/qml/qmlspectrogram/main.qml +++ b/examples/datavisualization/qmlspectrogram/qml/qmlspectrogram/main.qml @@ -58,7 +58,6 @@ Window { labelFormat: "%i" title: "Angle" titleVisible: true - labelAutoRotation: 30 titleFixed: false } @@ -78,7 +77,6 @@ Window { labelFormat: "%i" title: "Radius" titleVisible: true - labelAutoRotation: 30 titleFixed: false } @@ -101,6 +99,7 @@ Window { orthoProjection: true flipHorizontalGrid: true + radialLabelOffset: 0.01 // Add little offset so the labels do not overlap Surface3DSeries { id: surfaceSeries @@ -130,15 +129,15 @@ Window { id: polarToggle Layout.fillWidth: true Layout.fillHeight: true - text: "Switch to Polar" + text: "Switch to polar" //! [1] onClicked: { if (surfaceGraph.polar === false) { surfaceGraph.polar = true - text = "Switch to Cartesian" + text = "Switch to cartesian" } else { surfaceGraph.polar = false - text = "Switch to Polar" + text = "Switch to polar" } } //! [1] @@ -202,12 +201,12 @@ Window { Layout.fillWidth: true Layout.fillHeight: true text: "Toggle radial label position" - //visible: surfaceGraph.polar + visible: surfaceGraph.polar onClicked: { - // TODO - if (surfaceGraph.theme.backgroundEnabled === true) { + if (surfaceGraph.radialLabelOffset > 1.0) { + surfaceGraph.radialLabelOffset = 0.01 } else { - surfaceGraph.theme.backgroundEnabled = true; + surfaceGraph.radialLabelOffset = 1.06 } } } diff --git a/src/datavisualization/doc/src/qtdatavisualization-qml-abstractdeclarative.qdoc b/src/datavisualization/doc/src/qtdatavisualization-qml-abstractdeclarative.qdoc index 5afbc8c7..cae9a406 100644 --- a/src/datavisualization/doc/src/qtdatavisualization-qml-abstractdeclarative.qdoc +++ b/src/datavisualization/doc/src/qtdatavisualization-qml-abstractdeclarative.qdoc @@ -162,7 +162,20 @@ * * Defaults to \c{false}. * - * \sa orthoProjection, AbstractAxis3D::gridOffset, radialLabelOffset + * \sa orthoProjection, radialLabelOffset + */ + +/*! + * \qmlproperty real AbstractGraph3D::radialLabelOffset + * \since QtDataVisualization 1.2 + * + * This property specifies the normalized horizontal offset for the axis labels of the radial + * polar axis. The value 0.0 indicates the labels should be drawn next to the 0-angle angular + * axis grid line. The value 1.0 indicates the labels are drawn on their normal place at the edge + * of the graph background. + * This property is ignored if polar property value is \c{false}. Defaults to 1.0. + * + * \sa polar */ /*! diff --git a/src/datavisualization/doc/src/qtdatavisualization-qml-surface3d.qdoc b/src/datavisualization/doc/src/qtdatavisualization-qml-surface3d.qdoc index 4c25cc0a..4188e2b1 100644 --- a/src/datavisualization/doc/src/qtdatavisualization-qml-surface3d.qdoc +++ b/src/datavisualization/doc/src/qtdatavisualization-qml-surface3d.qdoc @@ -87,6 +87,7 @@ /*! * \qmlproperty bool Surface3D::flipHorizontalGrid + * \since QtDataVisualization 1.2 * * If \c{false}, the horizontal axis grid and labels are drawn on the horizontal background * of the graph. diff --git a/src/datavisualization/engine/abstract3dcontroller.cpp b/src/datavisualization/engine/abstract3dcontroller.cpp index e4fd0003..6b090fcd 100644 --- a/src/datavisualization/engine/abstract3dcontroller.cpp +++ b/src/datavisualization/engine/abstract3dcontroller.cpp @@ -51,6 +51,7 @@ Abstract3DController::Abstract3DController(QRect initialViewport, Q3DScene *scen m_isSeriesVisualsDirty(true), m_renderPending(false), m_isPolar(false), + m_radialLabelOffset(1.0f), m_measureFps(false), m_numFrames(0), m_currentFps(0.0) @@ -182,6 +183,11 @@ void Abstract3DController::synchDataToRenderer() m_changeTracker.polarChanged = false; } + if (m_changeTracker.radialLabelOffsetChanged) { + m_renderer->updateRadialLabelOffset(m_radialLabelOffset); + m_changeTracker.radialLabelOffsetChanged = false; + } + if (m_changeTracker.shadowQualityChanged) { m_renderer->updateShadowQuality(m_shadowQuality); m_changeTracker.shadowQualityChanged = false; @@ -1543,4 +1549,19 @@ bool Abstract3DController::isPolar() const return m_isPolar; } +void Abstract3DController::setRadialLabelOffset(float offset) +{ + if (m_radialLabelOffset != offset) { + m_radialLabelOffset = offset; + m_changeTracker.radialLabelOffsetChanged = true; + emit radialLabelOffsetChanged(m_radialLabelOffset); + emitNeedRender(); + } +} + +float Abstract3DController::radialLabelOffset() const +{ + return m_radialLabelOffset; +} + QT_END_NAMESPACE_DATAVISUALIZATION diff --git a/src/datavisualization/engine/abstract3dcontroller_p.h b/src/datavisualization/engine/abstract3dcontroller_p.h index 7d721b0c..6394da1e 100644 --- a/src/datavisualization/engine/abstract3dcontroller_p.h +++ b/src/datavisualization/engine/abstract3dcontroller_p.h @@ -91,6 +91,7 @@ struct Abstract3DChangeBitField { bool axisYTitleFixedChanged : 1; bool axisZTitleFixedChanged : 1; bool polarChanged : 1; + bool radialLabelOffsetChanged : 1; Abstract3DChangeBitField() : themeChanged(true), @@ -135,7 +136,8 @@ struct Abstract3DChangeBitField { axisXTitleFixedChanged(true), axisYTitleFixedChanged(true), axisZTitleFixedChanged(true), - polarChanged(true) + polarChanged(true), + radialLabelOffsetChanged(true) { } }; @@ -178,6 +180,7 @@ protected: bool m_isSeriesVisualsDirty; bool m_renderPending; bool m_isPolar; + float m_radialLabelOffset; QList m_seriesList; @@ -274,6 +277,8 @@ public: float aspectRatio(); void setPolar(bool enable); bool isPolar() const; + void setRadialLabelOffset(float offset); + float radialLabelOffset() const; void emitNeedRender(); @@ -352,6 +357,7 @@ signals: void aspectRatioChanged(float ratio); void optimizationHintsChanged(QAbstract3DGraph::OptimizationHints hints); void polarChanged(bool enabled); + void radialLabelOffsetChanged(float offset); protected: virtual QAbstract3DAxis *createDefaultAxis(QAbstract3DAxis::AxisOrientation orientation); diff --git a/src/datavisualization/engine/abstract3drenderer.cpp b/src/datavisualization/engine/abstract3drenderer.cpp index f7cb5499..f520e279 100644 --- a/src/datavisualization/engine/abstract3drenderer.cpp +++ b/src/datavisualization/engine/abstract3drenderer.cpp @@ -67,6 +67,7 @@ Abstract3DRenderer::Abstract3DRenderer(Abstract3DController *controller) m_labelObj(0), m_graphAspectRatio(2.0f), m_polarGraph(false), + m_radialLabelOffset(1.0f), m_xRightAngleRotation(QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, 90.0f)), m_yRightAngleRotation(QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, 90.0f)), m_zRightAngleRotation(QQuaternion::fromAxisAndAngle(0.0f, 0.0f, 1.0f, 90.0f)), @@ -316,6 +317,11 @@ void Abstract3DRenderer::updatePolar(bool enable) updateCustomItemPositions(); } +void Abstract3DRenderer::updateRadialLabelOffset(float offset) +{ + m_radialLabelOffset = offset; +} + void Abstract3DRenderer::updateOptimizationHint(QAbstract3DGraph::OptimizationHints hint) { m_cachedOptimizationHint = hint; diff --git a/src/datavisualization/engine/abstract3drenderer_p.h b/src/datavisualization/engine/abstract3drenderer_p.h index 10e88f3f..ed26a4ec 100644 --- a/src/datavisualization/engine/abstract3drenderer_p.h +++ b/src/datavisualization/engine/abstract3drenderer_p.h @@ -126,6 +126,7 @@ public: virtual void updateAspectRatio(float ratio); virtual void updatePolar(bool enable); + virtual void updateRadialLabelOffset(float offset); virtual QVector3D convertPositionToTranslation(const QVector3D &position, bool isAbsolute) = 0; @@ -254,6 +255,7 @@ protected: float m_graphAspectRatio; bool m_polarGraph; + float m_radialLabelOffset; QQuaternion m_xRightAngleRotation; QQuaternion m_yRightAngleRotation; diff --git a/src/datavisualization/engine/q3dsurface.cpp b/src/datavisualization/engine/q3dsurface.cpp index c9af656f..90b336df 100644 --- a/src/datavisualization/engine/q3dsurface.cpp +++ b/src/datavisualization/engine/q3dsurface.cpp @@ -233,6 +233,7 @@ QSurface3DSeries *Q3DSurface::selectedSeries() const /*! * \property Q3DSurface::flipHorizontalGrid + * \since QtDataVisualization 1.2 * * If \c{false}, the horizontal axis grid and labels are drawn on the horizontal background * of the graph. diff --git a/src/datavisualization/engine/qabstract3dgraph.cpp b/src/datavisualization/engine/qabstract3dgraph.cpp index 6240f714..af809100 100644 --- a/src/datavisualization/engine/qabstract3dgraph.cpp +++ b/src/datavisualization/engine/qabstract3dgraph.cpp @@ -662,7 +662,7 @@ QAbstract3DGraph::OptimizationHints QAbstract3DGraph::optimizationHints() const * * Defaults to \c{false}. * - * \sa orthoProjection, QAbstract3DAxis::gridOffset, radialLabelOffset + * \sa orthoProjection, radialLabelOffset */ void QAbstract3DGraph::setPolar(bool enable) { @@ -674,6 +674,28 @@ bool QAbstract3DGraph::isPolar() const return d_ptr->m_visualController->isPolar(); } +/*! + * \property QAbstract3DGraph::radialLabelOffset + * \since QtDataVisualization 1.2 + * + * This property specifies the normalized horizontal offset for the axis labels of the radial + * polar axis. The value 0.0 indicates the labels should be drawn next to the 0-angle angular + * axis grid line. The value 1.0 indicates the labels are drawn on their normal place at the edge + * of the graph background. + * This property is ignored if polar property value is \c{false}. Defaults to 1.0. + * + * \sa polar + */ +void QAbstract3DGraph::setRadialLabelOffset(float offset) +{ + d_ptr->m_visualController->setRadialLabelOffset(offset); +} + +float QAbstract3DGraph::radialLabelOffset() const +{ + return d_ptr->m_visualController->radialLabelOffset(); +} + /*! * \internal */ @@ -826,6 +848,8 @@ void QAbstract3DGraphPrivate::setVisualController(Abstract3DController *controll &QAbstract3DGraph::aspectRatioChanged); QObject::connect(m_visualController, &Abstract3DController::polarChanged, q_ptr, &QAbstract3DGraph::polarChanged); + QObject::connect(m_visualController, &Abstract3DController::radialLabelOffsetChanged, q_ptr, + &QAbstract3DGraph::radialLabelOffsetChanged); } void QAbstract3DGraphPrivate::handleDevicePixelRatioChange() diff --git a/src/datavisualization/engine/qabstract3dgraph.h b/src/datavisualization/engine/qabstract3dgraph.h index ca673358..e6ebc42b 100644 --- a/src/datavisualization/engine/qabstract3dgraph.h +++ b/src/datavisualization/engine/qabstract3dgraph.h @@ -51,6 +51,7 @@ class QT_DATAVISUALIZATION_EXPORT QAbstract3DGraph : public QWindow, protected Q Q_PROPERTY(qreal aspectRatio READ aspectRatio WRITE setAspectRatio NOTIFY aspectRatioChanged) Q_PROPERTY(OptimizationHints optimizationHints READ optimizationHints WRITE setOptimizationHints NOTIFY optimizationHintsChanged) Q_PROPERTY(bool polar READ isPolar WRITE setPolar NOTIFY polarChanged) + Q_PROPERTY(float radialLabelOffset READ radialLabelOffset WRITE setRadialLabelOffset NOTIFY radialLabelOffsetChanged) protected: explicit QAbstract3DGraph(QAbstract3DGraphPrivate *d, const QSurfaceFormat *format, @@ -154,6 +155,9 @@ public: void setPolar(bool enable); bool isPolar() const; + void setRadialLabelOffset(float offset); + float radialLabelOffset() const; + protected: bool event(QEvent *event); void resizeEvent(QResizeEvent *event); @@ -178,6 +182,7 @@ signals: void aspectRatioChanged(qreal ratio); void optimizationHintsChanged(QAbstract3DGraph::OptimizationHints hints); void polarChanged(bool enabled); + void radialLabelOffsetChanged(float offset); private: Q_DISABLE_COPY(QAbstract3DGraph) diff --git a/src/datavisualization/engine/surface3drenderer.cpp b/src/datavisualization/engine/surface3drenderer.cpp index 5fd59e5e..6cf335b0 100644 --- a/src/datavisualization/engine/surface3drenderer.cpp +++ b/src/datavisualization/engine/surface3drenderer.cpp @@ -1050,15 +1050,19 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) else m_xFlipped = true; + m_yFlippedForGrid = m_yFlipped; if (m_flipHorizontalGrid) { - // Need to determine if camera is below graph top - float distanceToCenter = activeCamera->position().length() - / activeCamera->zoomLevel() / m_autoScaleAdjustment * 100.0f; - qreal cameraAngle = qreal(activeCamera->yRotation()) / 180.0 * M_PI; - float cameraYPos = float(qSin(cameraAngle)) * distanceToCenter; - m_yFlippedForGrid = cameraYPos < backgroundMargin; - } else { - m_yFlippedForGrid = m_yFlipped; + if (!m_useOrthoProjection) { + // Need to determine if camera is below graph top + float distanceToCenter = activeCamera->position().length() + / activeCamera->zoomLevel() / m_autoScaleAdjustment * 100.0f; + qreal cameraAngle = qreal(activeCamera->yRotation()) / 180.0 * M_PI; + float cameraYPos = float(qSin(cameraAngle)) * distanceToCenter; + m_yFlippedForGrid = cameraYPos < backgroundMargin; + } else if (m_useOrthoProjection && activeCamera->yRotation() == 0.0f) { + // With ortho we only need to flip at angle zero, to fix label autorotation angles + m_yFlippedForGrid = !m_yFlipped; + } } // calculate background rotation based on view matrix rotation @@ -1937,6 +1941,9 @@ void Surface3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa labelYTrans, 0.0f); + if (m_polarGraph) + labelTrans.setX(labelTrans.x() * m_radialLabelOffset); + if (m_zFlipped) { startIndex = 0; endIndex = labelCount; diff --git a/src/datavisualizationqml2/abstractdeclarative.cpp b/src/datavisualizationqml2/abstractdeclarative.cpp index 30acc9e4..5182e96a 100644 --- a/src/datavisualizationqml2/abstractdeclarative.cpp +++ b/src/datavisualizationqml2/abstractdeclarative.cpp @@ -331,6 +331,8 @@ void AbstractDeclarative::setSharedController(Abstract3DController *controller) &AbstractDeclarative::handleOptimizationHintChange); QObject::connect(m_controller.data(), &Abstract3DController::polarChanged, this, &AbstractDeclarative::polarChanged); + QObject::connect(m_controller.data(), &Abstract3DController::radialLabelOffsetChanged, this, + &AbstractDeclarative::radialLabelOffsetChanged); } void AbstractDeclarative::activateOpenGLContext(QQuickWindow *window) @@ -736,6 +738,16 @@ bool AbstractDeclarative::isPolar() const return m_controller->isPolar(); } +void AbstractDeclarative::setRadialLabelOffset(float offset) +{ + m_controller->setRadialLabelOffset(offset); +} + +float AbstractDeclarative::radialLabelOffset() const +{ + return m_controller->radialLabelOffset(); +} + void AbstractDeclarative::windowDestroyed(QObject *obj) { // Remove destroyed window from window lists diff --git a/src/datavisualizationqml2/abstractdeclarative_p.h b/src/datavisualizationqml2/abstractdeclarative_p.h index cd3d6134..379e346f 100644 --- a/src/datavisualizationqml2/abstractdeclarative_p.h +++ b/src/datavisualizationqml2/abstractdeclarative_p.h @@ -73,6 +73,7 @@ class AbstractDeclarative : public QQuickItem Q_PROPERTY(qreal aspectRatio READ aspectRatio WRITE setAspectRatio NOTIFY aspectRatioChanged REVISION 1) Q_PROPERTY(OptimizationHints optimizationHints READ optimizationHints WRITE setOptimizationHints NOTIFY optimizationHintsChanged REVISION 1) Q_PROPERTY(bool polar READ isPolar WRITE setPolar NOTIFY polarChanged REVISION 2) + Q_PROPERTY(float radialLabelOffset READ radialLabelOffset WRITE setRadialLabelOffset NOTIFY radialLabelOffsetChanged REVISION 2) public: enum SelectionFlag { @@ -197,6 +198,9 @@ public: void setPolar(bool enable); bool isPolar() const; + void setRadialLabelOffset(float offset); + float radialLabelOffset() const; + public slots: virtual void handleAxisXChanged(QAbstract3DAxis *axis) = 0; virtual void handleAxisYChanged(QAbstract3DAxis *axis) = 0; @@ -235,6 +239,7 @@ signals: Q_REVISION(1) void aspectRatioChanged(qreal ratio); Q_REVISION(1) void optimizationHintsChanged(AbstractDeclarative::OptimizationHints hints); Q_REVISION(2) void polarChanged(bool enabled); + Q_REVISION(2) void radialLabelOffsetChanged(float offset); private: QPointer m_controller; -- cgit v1.2.3 From 47cc882034d9062a32ad2fa96377565ae0ab93be Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Thu, 26 Jun 2014 10:59:04 +0300 Subject: Polar chart for scatter Task-number: QTRD-3184 Change-Id: Ie8e10050e58a3630eda87ec44fb342776d667ae6 Reviewed-by: Mika Salmela --- src/datavisualization/engine/scatter3drenderer.cpp | 531 ++++++++++----------- src/datavisualization/engine/scatter3drenderer_p.h | 6 +- src/datavisualization/engine/surface3drenderer.cpp | 49 +- src/datavisualization/engine/surface3drenderer_p.h | 1 - tests/scattertest/main.cpp | 34 +- tests/scattertest/scatterchart.cpp | 27 +- tests/scattertest/scatterchart.h | 4 + 7 files changed, 350 insertions(+), 302 deletions(-) diff --git a/src/datavisualization/engine/scatter3drenderer.cpp b/src/datavisualization/engine/scatter3drenderer.cpp index 944d9c9b..2935d02d 100644 --- a/src/datavisualization/engine/scatter3drenderer.cpp +++ b/src/datavisualization/engine/scatter3drenderer.cpp @@ -60,11 +60,13 @@ Scatter3DRenderer::Scatter3DRenderer(Scatter3DController *controller) m_shadowQualityToShader(100.0f), m_shadowQualityMultiplier(3), m_heightNormalizer(1.0f), - m_scaleFactor(0), + m_scaleX(0.0f), + m_scaleZ(0.0f), + m_scaleXWithBackground(0.0f), + m_scaleZWithBackground(0.0f), m_selectedItemIndex(Scatter3DController::invalidSelectionIndex()), m_selectedSeriesCache(0), m_oldSelectedSeriesCache(0), - m_areaSize(QSizeF(0.0, 0.0)), m_dotSizeScale(1.0f), m_hasHeightAdjustmentChanged(true), m_backgroundMargin(defaultMaxSize), @@ -248,6 +250,7 @@ void Scatter3DRenderer::updateSeries(const QList &seriesLis m_backgroundMargin = maxItemSize / itemScaler; else m_backgroundMargin = defaultMaxSize; + calculateSceneScalingFactors(); if (noSelection) { if (!selectionLabel().isEmpty()) @@ -445,7 +448,7 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) if (!optimizationDefault && ((drawingPoints && cache->bufferPoints()->indexCount() == 0) - || (!drawingPoints && cache->bufferObject()->indexCount() == 0))) { + || (!drawingPoints && cache->bufferObject()->indexCount() == 0))) { continue; } @@ -717,7 +720,7 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) if (!optimizationDefault && ((drawingPoints && cache->bufferPoints()->indexCount() == 0) - || (!drawingPoints && cache->bufferObject()->indexCount() == 0))) { + || (!drawingPoints && cache->bufferObject()->indexCount() == 0))) { continue; } @@ -987,21 +990,8 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) QMatrix4x4 MVPMatrix; QMatrix4x4 itModelMatrix; -#ifndef USE_UNIFORM_SCALING // Use this if we want to use autoscaling for x and z - GLfloat xScale = (m_graphAspectRatio * m_areaSize.width()) / m_scaleFactor - + m_backgroundMargin; - GLfloat zScale = (m_graphAspectRatio * m_areaSize.height()) / m_scaleFactor - + m_backgroundMargin; - if (m_maxItemSize > xScale) - xScale = m_maxItemSize; - if (m_maxItemSize > zScale) - zScale = m_maxItemSize; - QVector3D bgScale(xScale, 1.0f + m_backgroundMargin, zScale); -#else // ..and this if we want uniform scaling based on largest dimension - QVector3D bgScale((m_graphAspectRatio + m_backgroundMargin), - 1.0f + m_backgroundMargin, - (m_graphAspectRatio + m_backgroundMargin)); -#endif + QVector3D bgScale(m_scaleXWithBackground, 1.0f + m_backgroundMargin, + m_scaleZWithBackground); modelMatrix.scale(bgScale); // If we're viewing from below, background object must be flipped if (m_yFlipped) { @@ -1059,6 +1049,10 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) glDisable(GL_TEXTURE_2D); // Draw grid lines + QVector3D gridLineScaleX(m_scaleXWithBackground, gridLineWidth, gridLineWidth); + QVector3D gridLineScaleZ(gridLineWidth, gridLineWidth, m_scaleZWithBackground); + QVector3D gridLineScaleY(gridLineWidth, 1.0f + m_backgroundMargin, gridLineWidth); + if (m_cachedTheme->isGridEnabled()) { #if !(defined QT_OPENGL_ES_2) ShaderHelper *lineShader = m_backgroundShader; @@ -1098,122 +1092,108 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) else lineYRotation = m_yRightAngleRotation; - if (m_yFlipped) + if (m_yFlippedForGrid) lineXRotation = m_xRightAngleRotation; else lineXRotation = m_xRightAngleRotationNeg; GLfloat yFloorLinePosition = -1.0f - m_backgroundMargin + gridLineOffset; - if (m_yFlipped) + if (m_yFlippedForGrid) yFloorLinePosition = -yFloorLinePosition; // Rows (= Z) if (m_axisCacheZ.segmentCount() > 0) { // Floor lines int gridLineCount = m_axisCacheZ.gridLineCount(); + if (m_polarGraph) { + drawRadialGrid(lineShader, yFloorLinePosition, projectionViewMatrix, + depthProjectionViewMatrix); + } else { + for (int line = 0; line < gridLineCount; line++) { + QMatrix4x4 modelMatrix; + QMatrix4x4 MVPMatrix; + QMatrix4x4 itModelMatrix; -#ifndef USE_UNIFORM_SCALING // Use this if we want to use autoscaling for x and z - GLfloat xScale = (m_graphAspectRatio * m_areaSize.width()) / m_scaleFactor - + m_backgroundMargin; - if (m_maxItemSize > xScale) - xScale = m_maxItemSize; - QVector3D gridLineScaler(xScale, gridLineWidth, gridLineWidth); -#else // ..and this if we want uniform scaling based on largest dimension - QVector3D gridLineScaler((m_graphAspectRatio + m_backgroundMargin), - gridLineWidth, gridLineWidth); -#endif - - for (int line = 0; line < gridLineCount; line++) { - QMatrix4x4 modelMatrix; - QMatrix4x4 MVPMatrix; - QMatrix4x4 itModelMatrix; + modelMatrix.translate(0.0f, yFloorLinePosition, + m_axisCacheZ.gridLinePosition(line)); - modelMatrix.translate(0.0f, yFloorLinePosition, - m_axisCacheZ.gridLinePosition(line)); + modelMatrix.scale(gridLineScaleX); + itModelMatrix.scale(gridLineScaleX); - modelMatrix.scale(gridLineScaler); - itModelMatrix.scale(gridLineScaler); + modelMatrix.rotate(lineXRotation); + itModelMatrix.rotate(lineXRotation); - modelMatrix.rotate(lineXRotation); - itModelMatrix.rotate(lineXRotation); + MVPMatrix = projectionViewMatrix * modelMatrix; - MVPMatrix = projectionViewMatrix * modelMatrix; - - // Set the rest of the shader bindings - lineShader->setUniformValue(lineShader->model(), modelMatrix); - lineShader->setUniformValue(lineShader->nModel(), - itModelMatrix.inverted().transposed()); - lineShader->setUniformValue(lineShader->MVP(), MVPMatrix); + // Set the rest of the shader bindings + lineShader->setUniformValue(lineShader->model(), modelMatrix); + lineShader->setUniformValue(lineShader->nModel(), + itModelMatrix.inverted().transposed()); + lineShader->setUniformValue(lineShader->MVP(), MVPMatrix); #if !defined(QT_OPENGL_ES_2) - if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { - QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix; - // Set shadow shader bindings - lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix); - // Draw the object - m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture); - } else { - // Draw the object - m_drawer->drawObject(lineShader, m_gridLineObj); - } + if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { + QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix; + // Set shadow shader bindings + lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix); + // Draw the object + m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture); + } else { + // Draw the object + m_drawer->drawObject(lineShader, m_gridLineObj); + } #else - m_drawer->drawLine(lineShader); + m_drawer->drawLine(lineShader); #endif - } + } - // Side wall lines - gridLineScaler = QVector3D(gridLineWidth, 1.0f + m_backgroundMargin, gridLineWidth); -#ifndef USE_UNIFORM_SCALING - GLfloat lineXTrans = (m_graphAspectRatio * m_areaSize.width()) - / m_scaleFactor - gridLineOffset + m_backgroundMargin; - if (m_maxItemSize > lineXTrans) - lineXTrans = m_maxItemSize - gridLineOffset; -#else - GLfloat lineXTrans = m_graphAspectRatio + m_backgroundMargin - gridLineOffset; -#endif - if (!m_xFlipped) - lineXTrans = -lineXTrans; + // Side wall lines + GLfloat lineXTrans = m_scaleXWithBackground - gridLineOffset; - for (int line = 0; line < gridLineCount; line++) { - QMatrix4x4 modelMatrix; - QMatrix4x4 MVPMatrix; - QMatrix4x4 itModelMatrix; + if (!m_xFlipped) + lineXTrans = -lineXTrans; + + for (int line = 0; line < gridLineCount; line++) { + QMatrix4x4 modelMatrix; + QMatrix4x4 MVPMatrix; + QMatrix4x4 itModelMatrix; - modelMatrix.translate(lineXTrans, 0.0f, m_axisCacheZ.gridLinePosition(line)); + modelMatrix.translate(lineXTrans, 0.0f, m_axisCacheZ.gridLinePosition(line)); - modelMatrix.scale(gridLineScaler); - itModelMatrix.scale(gridLineScaler); + modelMatrix.scale(gridLineScaleY); + itModelMatrix.scale(gridLineScaleY); #if !defined(QT_OPENGL_ES_2) - modelMatrix.rotate(lineYRotation); - itModelMatrix.rotate(lineYRotation); + modelMatrix.rotate(lineYRotation); + itModelMatrix.rotate(lineYRotation); #else - modelMatrix.rotate(m_zRightAngleRotation); - itModelMatrix.rotate(m_zRightAngleRotation); + modelMatrix.rotate(m_zRightAngleRotation); + itModelMatrix.rotate(m_zRightAngleRotation); #endif - MVPMatrix = projectionViewMatrix * modelMatrix; + MVPMatrix = projectionViewMatrix * modelMatrix; - // Set the rest of the shader bindings - lineShader->setUniformValue(lineShader->model(), modelMatrix); - lineShader->setUniformValue(lineShader->nModel(), - itModelMatrix.inverted().transposed()); - lineShader->setUniformValue(lineShader->MVP(), MVPMatrix); + // Set the rest of the shader bindings + lineShader->setUniformValue(lineShader->model(), modelMatrix); + lineShader->setUniformValue(lineShader->nModel(), + itModelMatrix.inverted().transposed()); + lineShader->setUniformValue(lineShader->MVP(), MVPMatrix); #if !defined(QT_OPENGL_ES_2) - if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { - // Set shadow shader bindings - QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix; - lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix); - // Draw the object - m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture); - } else { - // Draw the object - m_drawer->drawObject(lineShader, m_gridLineObj); - } + if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { + // Set shadow shader bindings + QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix; + lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix); + // Draw the object + m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture); + } else { + // Draw the object + m_drawer->drawObject(lineShader, m_gridLineObj); + } #else - m_drawer->drawLine(lineShader); + m_drawer->drawLine(lineShader); #endif + } } } @@ -1225,111 +1205,97 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) // Floor lines int gridLineCount = m_axisCacheX.gridLineCount(); -#ifndef USE_UNIFORM_SCALING - GLfloat zScale = (m_graphAspectRatio * m_areaSize.height()) / m_scaleFactor - + m_backgroundMargin; - if (m_maxItemSize > zScale) - zScale = m_maxItemSize; - QVector3D gridLineScaler(gridLineWidth, gridLineWidth, zScale); -#else - QVector3D gridLineScaler(gridLineWidth, gridLineWidth, - m_graphAspectRatio + m_backgroundMargin); -#endif - - for (int line = 0; line < gridLineCount; line++) { - QMatrix4x4 modelMatrix; - QMatrix4x4 MVPMatrix; - QMatrix4x4 itModelMatrix; + if (m_polarGraph) { + drawAngularGrid(lineShader, yFloorLinePosition, projectionViewMatrix, + depthProjectionViewMatrix); + } else { + for (int line = 0; line < gridLineCount; line++) { + QMatrix4x4 modelMatrix; + QMatrix4x4 MVPMatrix; + QMatrix4x4 itModelMatrix; - modelMatrix.translate(m_axisCacheX.gridLinePosition(line), yFloorLinePosition, - 0.0f); + modelMatrix.translate(m_axisCacheX.gridLinePosition(line), yFloorLinePosition, + 0.0f); - modelMatrix.scale(gridLineScaler); - itModelMatrix.scale(gridLineScaler); + modelMatrix.scale(gridLineScaleZ); + itModelMatrix.scale(gridLineScaleZ); - modelMatrix.rotate(lineXRotation); - itModelMatrix.rotate(lineXRotation); + modelMatrix.rotate(lineXRotation); + itModelMatrix.rotate(lineXRotation); - MVPMatrix = projectionViewMatrix * modelMatrix; + MVPMatrix = projectionViewMatrix * modelMatrix; - // Set the rest of the shader bindings - lineShader->setUniformValue(lineShader->model(), modelMatrix); - lineShader->setUniformValue(lineShader->nModel(), - itModelMatrix.inverted().transposed()); - lineShader->setUniformValue(lineShader->MVP(), MVPMatrix); + // Set the rest of the shader bindings + lineShader->setUniformValue(lineShader->model(), modelMatrix); + lineShader->setUniformValue(lineShader->nModel(), + itModelMatrix.inverted().transposed()); + lineShader->setUniformValue(lineShader->MVP(), MVPMatrix); #if !defined(QT_OPENGL_ES_2) - if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { - // Set shadow shader bindings - QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix; - lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix); - // Draw the object - m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture); - } else { - // Draw the object - m_drawer->drawObject(lineShader, m_gridLineObj); - } + if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { + // Set shadow shader bindings + QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix; + lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix); + // Draw the object + m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture); + } else { + // Draw the object + m_drawer->drawObject(lineShader, m_gridLineObj); + } #else - m_drawer->drawLine(lineShader); + m_drawer->drawLine(lineShader); #endif - } + } - // Back wall lines -#ifndef USE_UNIFORM_SCALING - GLfloat lineZTrans = (m_graphAspectRatio * m_areaSize.height()) - / m_scaleFactor - gridLineOffset + m_backgroundMargin; - if (m_maxItemSize > lineZTrans) - lineZTrans = m_maxItemSize - gridLineOffset; -#else - GLfloat lineZTrans = m_graphAspectRatio + m_backgroundMargin - gridLineOffset; -#endif - if (!m_zFlipped) - lineZTrans = -lineZTrans; + // Back wall lines + GLfloat lineZTrans = m_scaleZWithBackground - gridLineOffset; - gridLineScaler = QVector3D(gridLineWidth, 1.0f + m_backgroundMargin, gridLineWidth); + if (!m_zFlipped) + lineZTrans = -lineZTrans; - for (int line = 0; line < gridLineCount; line++) { - QMatrix4x4 modelMatrix; - QMatrix4x4 MVPMatrix; - QMatrix4x4 itModelMatrix; + for (int line = 0; line < gridLineCount; line++) { + QMatrix4x4 modelMatrix; + QMatrix4x4 MVPMatrix; + QMatrix4x4 itModelMatrix; - modelMatrix.translate(m_axisCacheX.gridLinePosition(line), 0.0f, lineZTrans); + modelMatrix.translate(m_axisCacheX.gridLinePosition(line), 0.0f, lineZTrans); - modelMatrix.scale(gridLineScaler); - itModelMatrix.scale(gridLineScaler); + modelMatrix.scale(gridLineScaleY); + itModelMatrix.scale(gridLineScaleY); #if !defined(QT_OPENGL_ES_2) - if (m_zFlipped) { - modelMatrix.rotate(m_xFlipRotation); - itModelMatrix.rotate(m_xFlipRotation); - } + if (m_zFlipped) { + modelMatrix.rotate(m_xFlipRotation); + itModelMatrix.rotate(m_xFlipRotation); + } #else - modelMatrix.rotate(m_zRightAngleRotation); - itModelMatrix.rotate(m_zRightAngleRotation); + modelMatrix.rotate(m_zRightAngleRotation); + itModelMatrix.rotate(m_zRightAngleRotation); #endif - MVPMatrix = projectionViewMatrix * modelMatrix; + MVPMatrix = projectionViewMatrix * modelMatrix; - // Set the rest of the shader bindings - lineShader->setUniformValue(lineShader->model(), modelMatrix); - lineShader->setUniformValue(lineShader->nModel(), - itModelMatrix.inverted().transposed()); - lineShader->setUniformValue(lineShader->MVP(), MVPMatrix); + // Set the rest of the shader bindings + lineShader->setUniformValue(lineShader->model(), modelMatrix); + lineShader->setUniformValue(lineShader->nModel(), + itModelMatrix.inverted().transposed()); + lineShader->setUniformValue(lineShader->MVP(), MVPMatrix); #if !defined(QT_OPENGL_ES_2) - if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { - // Set shadow shader bindings - QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix; - lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix); - // Draw the object - m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture); - } else { - // Draw the object - m_drawer->drawObject(lineShader, m_gridLineObj); - } + if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { + // Set shadow shader bindings + QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix; + lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix); + // Draw the object + m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture); + } else { + // Draw the object + m_drawer->drawObject(lineShader, m_gridLineObj); + } #else - m_drawer->drawLine(lineShader); + m_drawer->drawLine(lineShader); #endif + } } } @@ -1338,21 +1304,8 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) // Back wall int gridLineCount = m_axisCacheY.gridLineCount(); -#ifndef USE_UNIFORM_SCALING // Use this if we want to use autoscaling for x and z - GLfloat lineZTrans = (m_graphAspectRatio * m_areaSize.height()) - / m_scaleFactor - gridLineOffset + m_backgroundMargin; - if (m_maxItemSize > lineZTrans) - lineZTrans = m_maxItemSize - gridLineOffset; - GLfloat xScale = (m_graphAspectRatio * m_areaSize.width()) / m_scaleFactor - + m_backgroundMargin; - if (m_maxItemSize > xScale) - xScale = m_maxItemSize; - QVector3D gridLineScaler(xScale, gridLineWidth, gridLineWidth); -#else // ..and this if we want uniform scaling based on largest dimension - GLfloat lineZTrans = m_graphAspectRatio + m_backgroundMargin - gridLineOffset; - QVector3D gridLineScaler((m_graphAspectRatio + m_backgroundMargin), - gridLineWidth, gridLineWidth); -#endif + GLfloat lineZTrans = m_scaleZWithBackground - gridLineOffset; + if (!m_zFlipped) lineZTrans = -lineZTrans; @@ -1363,8 +1316,8 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) modelMatrix.translate(0.0f, m_axisCacheY.gridLinePosition(line), lineZTrans); - modelMatrix.scale(gridLineScaler); - itModelMatrix.scale(gridLineScaler); + modelMatrix.scale(gridLineScaleX); + itModelMatrix.scale(gridLineScaleX); if (m_zFlipped) { modelMatrix.rotate(m_xFlipRotation); @@ -1396,21 +1349,8 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) } // Side wall -#ifndef USE_UNIFORM_SCALING // Use this if we want to use autoscaling for x and z - GLfloat lineXTrans = (m_graphAspectRatio * m_areaSize.width()) - / m_scaleFactor - gridLineOffset + m_backgroundMargin; - if (m_maxItemSize > lineXTrans) - lineXTrans = m_maxItemSize - gridLineOffset; - GLfloat zScale = (m_graphAspectRatio * m_areaSize.height()) - / m_scaleFactor + m_backgroundMargin; - if (m_maxItemSize > zScale) - zScale = m_maxItemSize; - gridLineScaler = QVector3D(gridLineWidth, gridLineWidth, zScale); -#else // ..and this if we want uniform scaling based on largest dimension - GLfloat lineXTrans = m_graphAspectRatio + m_backgroundMargin - gridLineOffset; - gridLineScaler = QVector3D(gridLineWidth, gridLineWidth, - m_graphAspectRatio + m_backgroundMargin); -#endif + GLfloat lineXTrans = m_scaleXWithBackground - gridLineOffset; + if (!m_xFlipped) lineXTrans = -lineXTrans; @@ -1421,8 +1361,8 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) modelMatrix.translate(lineXTrans, m_axisCacheY.gridLinePosition(line), 0.0f); - modelMatrix.scale(gridLineScaler); - itModelMatrix.scale(gridLineScaler); + modelMatrix.scale(gridLineScaleZ); + itModelMatrix.scale(gridLineScaleZ); modelMatrix.rotate(lineYRotation); itModelMatrix.rotate(lineYRotation); @@ -1532,15 +1472,14 @@ void Scatter3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa // Z Labels if (m_axisCacheZ.segmentCount() > 0) { int labelCount = m_axisCacheZ.labelCount(); -#ifndef USE_UNIFORM_SCALING - GLfloat labelXTrans = (m_graphAspectRatio * m_areaSize.width()) - / m_scaleFactor + labelMargin + m_backgroundMargin; - if (m_maxItemSize > labelXTrans) - labelXTrans = m_maxItemSize + labelMargin; -#else - GLfloat labelXTrans = m_graphAspectRatio + m_backgroundMargin + labelMargin; -#endif - GLfloat labelYTrans = -1.0f - m_backgroundMargin; + float labelXTrans = m_scaleXWithBackground + labelMargin; + float labelYTrans = -1.0f - m_backgroundMargin; + if (m_polarGraph) { + labelXTrans *= m_radialLabelOffset; + // YTrans up only if over background + if (m_radialLabelOffset < 1.0f) + labelYTrans += gridLineOffset + gridLineWidth; + } Qt::AlignmentFlag alignment = (m_xFlipped == m_zFlipped) ? Qt::AlignLeft : Qt::AlignRight; QVector3D labelRotation; if (m_xFlipped) @@ -1550,7 +1489,7 @@ void Scatter3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa if (labelAutoAngle == 0.0f) { if (m_zFlipped) labelRotation.setY(180.0f); - if (m_yFlipped) { + if (m_yFlippedForGrid) { if (m_zFlipped) labelRotation.setY(180.0f); else @@ -1562,7 +1501,7 @@ void Scatter3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa } else { if (m_zFlipped) labelRotation.setY(180.0f); - if (m_yFlipped) { + if (m_yFlippedForGrid) { if (m_zFlipped) { if (m_xFlipped) { labelRotation.setX(90.0f - (labelAutoAngle - fractionCamX) @@ -1621,13 +1560,18 @@ void Scatter3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa } float offsetValue = 0.0f; for (int label = startIndex; label != endIndex; label = label + indexStep) { - labelTrans.setZ(m_axisCacheZ.labelPosition(label)); - glPolygonOffset(offsetValue++ / -10.0f, 1.0f); - + const LabelItem &axisLabelItem = *m_axisCacheZ.labelItems().at(label); // Draw the label here + if (m_polarGraph) { + float direction = m_zFlipped ? -1.0f : 1.0f; + labelTrans.setZ((m_axisCacheZ.formatter()->labelPositions().at(label) + * -m_graphAspectRatio + + m_drawer->scaledFontSize() + gridLineWidth) * direction); + } else { + labelTrans.setZ(m_axisCacheZ.labelPosition(label)); + } m_dummyRenderItem.setTranslation(labelTrans); - const LabelItem &axisLabelItem = *m_axisCacheZ.labelItems().at(label); if (drawSelection) { QVector4D labelColor = QVector4D(label / 255.0f, 0.0f, 0.0f, @@ -1656,15 +1600,13 @@ void Scatter3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa fractionCamY = activeCamera->yRotation() * labelAngleFraction; fractionCamX = activeCamera->xRotation() * labelAngleFraction; int labelCount = m_axisCacheX.labelCount(); -#ifndef USE_UNIFORM_SCALING - GLfloat labelZTrans = (m_graphAspectRatio * m_areaSize.height()) - / m_scaleFactor + labelMargin + m_backgroundMargin; - if (m_maxItemSize > labelZTrans) - labelZTrans = m_maxItemSize + labelMargin; -#else - GLfloat labelZTrans = m_graphAspectRatio + m_backgroundMargin + labelMargin; -#endif - GLfloat labelYTrans = -1.0f - m_backgroundMargin; + float labelZTrans = 0.0f; + float labelYTrans = -1.0f - m_backgroundMargin; + if (m_polarGraph) + labelYTrans += gridLineOffset + gridLineWidth; + else + labelZTrans = m_scaleZWithBackground + labelMargin; + Qt::AlignmentFlag alignment = (m_xFlipped != m_zFlipped) ? Qt::AlignLeft : Qt::AlignRight; QVector3D labelRotation; if (m_zFlipped) @@ -1675,7 +1617,7 @@ void Scatter3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa labelRotation = QVector3D(-90.0f, 90.0f, 0.0f); if (m_xFlipped) labelRotation.setY(-90.0f); - if (m_yFlipped) { + if (m_yFlippedForGrid) { if (m_xFlipped) labelRotation.setY(-90.0f); else @@ -1687,7 +1629,7 @@ void Scatter3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa labelRotation.setY(-90.0f); else labelRotation.setY(90.0f); - if (m_yFlipped) { + if (m_yFlippedForGrid) { if (m_zFlipped) { if (m_xFlipped) { labelRotation.setX(90.0f - (2.0f * labelAutoAngle - fractionCamX) @@ -1735,6 +1677,14 @@ void Scatter3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa } QQuaternion totalRotation = Utils::calculateRotation(labelRotation); + if (m_polarGraph) { + if (!m_yFlippedForGrid && (m_zFlipped != m_xFlipped) + || m_yFlippedForGrid && (m_zFlipped == m_xFlipped)) { + totalRotation *= m_zRightAngleRotation; + } else { + totalRotation *= m_zRightAngleRotationNeg; + } + } QVector3D labelTrans = QVector3D(0.0f, labelYTrans, labelZTrans); if (m_xFlipped) { startIndex = labelCount - 1; @@ -1746,12 +1696,45 @@ void Scatter3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa indexStep = 1; } float offsetValue = 0.0f; - for (int label = startIndex; label != endIndex; label = label + indexStep) { - labelTrans.setX(m_axisCacheX.labelPosition(label)); + bool showLastLabel = false; + QVector &gridPositions = m_axisCacheX.formatter()->gridPositions(); + int lastGridPosIndex = gridPositions.size() - 1; + if (gridPositions.size() + && (gridPositions.at(lastGridPosIndex) != 1.0f || gridPositions.at(0) != 0.0f)) { + // Avoid overlapping first and last label if they would get on same position + showLastLabel = true; + } + for (int label = startIndex; label != endIndex; label = label + indexStep) { glPolygonOffset(offsetValue++ / -10.0f, 1.0f); - // Draw the label here + if (m_polarGraph) { + // Calculate angular position + if (label == lastGridPosIndex && !showLastLabel) + continue; + float gridPosition = gridPositions.at(label); + qreal angle = gridPosition * M_PI * 2.0; + labelTrans.setX((m_graphAspectRatio + labelMargin)* float(qSin(angle))); + labelTrans.setZ(-(m_graphAspectRatio + labelMargin) * float(qCos(angle))); + // Alignment depends on label angular position, as well as flips + Qt::AlignmentFlag vAlignment = Qt::AlignCenter; + Qt::AlignmentFlag hAlignment = Qt::AlignCenter; + const float centerMargin = 0.005f; + if (gridPosition < 0.25f - centerMargin || gridPosition > 0.75f + centerMargin) + vAlignment = m_zFlipped ? Qt::AlignTop : Qt::AlignBottom; + else if (gridPosition > 0.25f + centerMargin && gridPosition < 0.75f - centerMargin) + vAlignment = m_zFlipped ? Qt::AlignBottom : Qt::AlignTop; + + if (gridPosition < 0.50f - centerMargin && gridPosition > centerMargin) + hAlignment = m_zFlipped ? Qt::AlignRight : Qt::AlignLeft; + else if (gridPosition < 1.0f - centerMargin && gridPosition > 0.5f + centerMargin) + hAlignment = m_zFlipped ? Qt::AlignLeft : Qt::AlignRight; + if (m_yFlippedForGrid && vAlignment != Qt::AlignCenter) + vAlignment = (vAlignment == Qt::AlignTop) ? Qt::AlignBottom : Qt::AlignTop; + alignment = Qt::AlignmentFlag(vAlignment | hAlignment); + } else { + labelTrans.setX(m_axisCacheX.labelPosition(label)); + } m_dummyRenderItem.setTranslation(labelTrans); const LabelItem &axisLabelItem = *m_axisCacheX.labelItems().at(label); @@ -1782,22 +1765,13 @@ void Scatter3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa fractionCamY = activeCamera->yRotation() * labelAngleFraction; fractionCamX = activeCamera->xRotation() * labelAngleFraction; int labelCount = m_axisCacheY.labelCount(); -#ifndef USE_UNIFORM_SCALING // Use this if we want to use autoscaling for x and z - GLfloat labelXTrans = (m_graphAspectRatio* m_areaSize.width()) - / m_scaleFactor + m_backgroundMargin; - GLfloat labelZTrans = (m_graphAspectRatio * m_areaSize.height()) - / m_scaleFactor + m_backgroundMargin; - if (m_maxItemSize > labelXTrans) - labelXTrans = m_maxItemSize; - if (m_maxItemSize > labelZTrans) - labelZTrans = m_maxItemSize; -#else // ..and this if we want uniform scaling based on largest dimension - GLfloat labelXTrans = m_graphAspectRatio + m_backgroundMargin; - GLfloat labelZTrans = labelXTrans; -#endif + + float labelXTrans = m_scaleXWithBackground; + float labelZTrans = m_scaleZWithBackground; + // Back & side wall - GLfloat labelMarginXTrans = labelMargin; - GLfloat labelMarginZTrans = labelMargin; + float labelMarginXTrans = labelMargin; + float labelMarginZTrans = labelMargin; QVector3D backLabelRotation(0.0f, -90.0f, 0.0f); QVector3D sideLabelRotation(0.0f, 0.0f, 0.0f); Qt::AlignmentFlag backAlignment = @@ -1854,7 +1828,7 @@ void Scatter3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa float offsetValue = 0.0f; for (int label = startIndex; label != endIndex; label = label + indexStep) { const LabelItem &axisLabelItem = *m_axisCacheY.labelItems().at(label); - const GLfloat labelYTrans = m_axisCacheY.labelPosition(label); + const float labelYTrans = m_axisCacheY.labelPosition(label); glPolygonOffset(offsetValue++ / -10.0f, 1.0f); @@ -1991,27 +1965,44 @@ void Scatter3DRenderer::calculateTranslation(ScatterRenderItem &item) { // We need to normalize translations const QVector3D &pos = item.position(); - float xTrans = m_axisCacheX.positionAt(pos.x()); + float xTrans; float yTrans = m_axisCacheY.positionAt(pos.y()); - float zTrans = m_axisCacheZ.positionAt(pos.z()); + float zTrans; + if (m_polarGraph) { + calculatePolarXZ(pos, xTrans, zTrans); + } else { + xTrans = m_axisCacheX.positionAt(pos.x()); + zTrans = m_axisCacheZ.positionAt(pos.z()); + } item.setTranslation(QVector3D(xTrans, yTrans, zTrans)); } void Scatter3DRenderer::calculateSceneScalingFactors() { m_heightNormalizer = GLfloat(m_axisCacheY.max() - m_axisCacheY.min()) / 2.0f; - m_areaSize.setHeight((m_axisCacheZ.max() - m_axisCacheZ.min()) / 2.0f); - m_areaSize.setWidth((m_axisCacheX.max() - m_axisCacheX.min()) / 2.0f); - m_scaleFactor = qMax(m_areaSize.width(), m_areaSize.height()); - #ifndef USE_UNIFORM_SCALING // Use this if we want to use autoscaling for x and z - float factorScaler = 2.0f * m_graphAspectRatio / m_scaleFactor; - m_axisCacheX.setScale(factorScaler * m_areaSize.width()); - m_axisCacheZ.setScale(-factorScaler * m_areaSize.height()); + QSizeF areaSize; + areaSize.setHeight((m_axisCacheZ.max() - m_axisCacheZ.min())); + areaSize.setWidth((m_axisCacheX.max() - m_axisCacheX.min())); + float scaleFactor = qMax(areaSize.width(), areaSize.height()); + if (m_polarGraph) { + m_scaleX = m_graphAspectRatio; + m_scaleZ = m_graphAspectRatio; + } else { + m_scaleX = m_graphAspectRatio * areaSize.width() / scaleFactor; + m_scaleZ = m_graphAspectRatio * areaSize.height() / scaleFactor; + } + float factorScaler = 2.0f * m_graphAspectRatio / scaleFactor; + m_axisCacheX.setScale(factorScaler * areaSize.width()); + m_axisCacheZ.setScale(-factorScaler * areaSize.height()); #else // ..and this if we want uniform scaling based on largest dimension + m_scaleX = m_graphAspectRatio; + m_scaleZ = m_graphAspectRatio; m_axisCacheX.setScale(2.0f * m_graphAspectRatio); m_axisCacheZ.setScale(-m_axisCacheX.scale()); #endif + m_scaleXWithBackground = m_scaleX + m_backgroundMargin; + m_scaleZWithBackground = m_scaleZ + m_backgroundMargin; m_axisCacheX.setTranslate(-m_axisCacheX.scale() / 2.0f); m_axisCacheZ.setTranslate(-m_axisCacheZ.scale() / 2.0f); } diff --git a/src/datavisualization/engine/scatter3drenderer_p.h b/src/datavisualization/engine/scatter3drenderer_p.h index b0b1e411..b5d188e1 100644 --- a/src/datavisualization/engine/scatter3drenderer_p.h +++ b/src/datavisualization/engine/scatter3drenderer_p.h @@ -68,11 +68,13 @@ private: GLfloat m_shadowQualityToShader; GLint m_shadowQualityMultiplier; GLfloat m_heightNormalizer; - GLfloat m_scaleFactor; + float m_scaleX; + float m_scaleZ; + float m_scaleXWithBackground; + float m_scaleZWithBackground; int m_selectedItemIndex; ScatterSeriesRenderCache *m_selectedSeriesCache; ScatterSeriesRenderCache *m_oldSelectedSeriesCache; - QSizeF m_areaSize; GLfloat m_dotSizeScale; bool m_hasHeightAdjustmentChanged; ScatterRenderItem m_dummyRenderItem; diff --git a/src/datavisualization/engine/surface3drenderer.cpp b/src/datavisualization/engine/surface3drenderer.cpp index 6cf335b0..1558b636 100644 --- a/src/datavisualization/engine/surface3drenderer.cpp +++ b/src/datavisualization/engine/surface3drenderer.cpp @@ -1596,7 +1596,7 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) if (m_polarGraph) { drawAngularGrid(lineShader, yFloorLinePosition, projectionViewMatrix, - depthProjectionViewMatrix); + depthProjectionViewMatrix); } else { for (int line = 0; line < gridLineCount; line++) { QMatrix4x4 modelMatrix; @@ -1858,14 +1858,13 @@ void Surface3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa QVector3D positionZComp(0.0f, 0.0f, 0.0f); if (m_axisCacheZ.segmentCount() > 0) { int labelCount = m_axisCacheZ.labelCount(); - float labelXTrans = 0.0f; + float labelXTrans = m_scaleXWithBackground + labelMargin; float labelYTrans = m_flipHorizontalGrid ? backgroundMargin : -backgroundMargin; if (m_polarGraph) { - // TODO optional placement of radial labels - YTrans up only if over background - labelXTrans = m_scaleXWithBackground + labelMargin; - //labelYTrans += gridLineOffset + gridLineWidth; - } else { - labelXTrans = m_scaleXWithBackground + labelMargin; + labelXTrans *= m_radialLabelOffset; + // YTrans up only if over background + if (m_radialLabelOffset < 1.0f) + labelYTrans += gridLineOffset + gridLineWidth; } Qt::AlignmentFlag alignment = (m_xFlipped == m_zFlipped) ? Qt::AlignLeft : Qt::AlignRight; QVector3D labelRotation; @@ -1941,9 +1940,6 @@ void Surface3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa labelYTrans, 0.0f); - if (m_polarGraph) - labelTrans.setX(labelTrans.x() * m_radialLabelOffset); - if (m_zFlipped) { startIndex = 0; endIndex = labelCount; @@ -1961,8 +1957,8 @@ void Surface3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa if (m_polarGraph) { float direction = m_zFlipped ? -1.0f : 1.0f; labelTrans.setZ((m_axisCacheZ.formatter()->labelPositions().at(label) - * -m_graphAspectRatio - + m_drawer->scaledFontSize() + gridLineWidth) * direction); + * -m_graphAspectRatio + + m_drawer->scaledFontSize() + gridLineWidth) * direction); } else { labelTrans.setZ(m_axisCacheZ.labelPosition(label)); } @@ -1995,7 +1991,7 @@ void Surface3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa fractionCamX = activeCamera->xRotation() * labelAngleFraction; int labelCount = m_axisCacheX.labelCount(); - GLfloat labelZTrans = 0.0f; + float labelZTrans = 0.0f; float labelYTrans = m_flipHorizontalGrid ? backgroundMargin : -backgroundMargin; if (m_polarGraph) labelYTrans += gridLineOffset + gridLineWidth; @@ -2111,7 +2107,7 @@ void Surface3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa // Calculate angular position if (label == lastGridPosIndex && !showLastLabel) continue; - float gridPosition = m_axisCacheX.formatter()->gridPositions().at(label); + float gridPosition = gridPositions.at(label); qreal angle = gridPosition * M_PI * 2.0; labelTrans.setX((m_graphAspectRatio + labelMargin)* float(qSin(angle))); labelTrans.setZ(-(m_graphAspectRatio + labelMargin) * float(qCos(angle))); @@ -2164,12 +2160,12 @@ void Surface3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa fractionCamX = activeCamera->xRotation() * labelAngleFraction; int labelCount = m_axisCacheY.labelCount(); - GLfloat labelXTrans = m_scaleXWithBackground; - GLfloat labelZTrans = m_scaleZWithBackground; + float labelXTrans = m_scaleXWithBackground; + float labelZTrans = m_scaleZWithBackground; // Back & side wall - GLfloat labelMarginXTrans = labelMargin; - GLfloat labelMarginZTrans = labelMargin; + float labelMarginXTrans = labelMargin; + float labelMarginZTrans = labelMargin; QVector3D backLabelRotation(0.0f, -90.0f, 0.0f); QVector3D sideLabelRotation(0.0f, 0.0f, 0.0f); Qt::AlignmentFlag backAlignment = @@ -2226,7 +2222,7 @@ void Surface3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa float offsetValue = 0.0f; for (int label = startIndex; label != endIndex; label = label + indexStep) { const LabelItem &axisLabelItem = *m_axisCacheY.labelItems().at(label); - const GLfloat labelYTrans = m_axisCacheY.labelPosition(label); + const float labelYTrans = m_axisCacheY.labelPosition(label); glPolygonOffset(offsetValue++ / -10.0f, 1.0f); @@ -2387,22 +2383,23 @@ void Surface3DRenderer::calculateSceneScalingFactors() { // Calculate scene scaling and translation factors m_heightNormalizer = GLfloat(m_axisCacheY.max() - m_axisCacheY.min()); - m_areaSize.setHeight(m_axisCacheZ.max() - m_axisCacheZ.min()); - m_areaSize.setWidth(m_axisCacheX.max() - m_axisCacheX.min()); - float scaleFactor = qMax(m_areaSize.width(), m_areaSize.height()); + QSizeF areaSize; + areaSize.setHeight(m_axisCacheZ.max() - m_axisCacheZ.min()); + areaSize.setWidth(m_axisCacheX.max() - m_axisCacheX.min()); + float scaleFactor = qMax(areaSize.width(), areaSize.height()); if (m_polarGraph) { m_scaleX = m_graphAspectRatio; m_scaleZ = m_graphAspectRatio; } else { - m_scaleX = m_graphAspectRatio * m_areaSize.width() / scaleFactor; - m_scaleZ = m_graphAspectRatio * m_areaSize.height() / scaleFactor; + m_scaleX = m_graphAspectRatio * areaSize.width() / scaleFactor; + m_scaleZ = m_graphAspectRatio * areaSize.height() / scaleFactor; } m_scaleXWithBackground = m_scaleX + backgroundMargin - 1.0f; m_scaleZWithBackground = m_scaleZ + backgroundMargin - 1.0f; float factorScaler = 2.0f * m_graphAspectRatio / scaleFactor; - m_axisCacheX.setScale(factorScaler * m_areaSize.width()); - m_axisCacheZ.setScale(-factorScaler * m_areaSize.height()); + m_axisCacheX.setScale(factorScaler * areaSize.width()); + m_axisCacheZ.setScale(-factorScaler * areaSize.height()); m_axisCacheX.setTranslate(-m_axisCacheX.scale() / 2.0f); m_axisCacheZ.setTranslate(-m_axisCacheZ.scale() / 2.0f); } diff --git a/src/datavisualization/engine/surface3drenderer_p.h b/src/datavisualization/engine/surface3drenderer_p.h index b1ebf33e..db174a0d 100644 --- a/src/datavisualization/engine/surface3drenderer_p.h +++ b/src/datavisualization/engine/surface3drenderer_p.h @@ -71,7 +71,6 @@ private: bool m_selectionActive; AbstractRenderItem m_dummyRenderItem; GLint m_shadowQualityMultiplier; - QSizeF m_areaSize; bool m_hasHeightAdjustmentChanged; QPoint m_selectedPoint; QSurface3DSeries *m_selectedSeries; diff --git a/tests/scattertest/main.cpp b/tests/scattertest/main.cpp index 207da530..f434e324 100644 --- a/tests/scattertest/main.cpp +++ b/tests/scattertest/main.cpp @@ -235,6 +235,18 @@ int main(int argc, char **argv) aspectRatioSlider->setValue(20); aspectRatioSlider->setMaximum(100); + QCheckBox *optimizationStaticCB = new QCheckBox(widget); + optimizationStaticCB->setText(QStringLiteral("Static optimization")); + optimizationStaticCB->setChecked(false); + + QCheckBox *orthoCB = new QCheckBox(widget); + orthoCB->setText(QStringLiteral("Orthogonal projection")); + orthoCB->setChecked(false); + + QCheckBox *polarCB = new QCheckBox(widget); + polarCB->setText(QStringLiteral("Polar graph")); + polarCB->setChecked(false); + QCheckBox *axisTitlesVisibleCB = new QCheckBox(widget); axisTitlesVisibleCB->setText(QStringLiteral("Axis titles visible")); axisTitlesVisibleCB->setChecked(false); @@ -250,6 +262,13 @@ int main(int argc, char **argv) axisLabelRotationSlider->setValue(0); axisLabelRotationSlider->setMaximum(90); + QSlider *radialLabelSlider = new QSlider(Qt::Horizontal, widget); + radialLabelSlider->setTickInterval(10); + radialLabelSlider->setTickPosition(QSlider::TicksBelow); + radialLabelSlider->setMinimum(0); + radialLabelSlider->setValue(100); + radialLabelSlider->setMaximum(150); + vLayout->addWidget(themeButton, 0, Qt::AlignTop); vLayout->addWidget(labelButton, 0, Qt::AlignTop); vLayout->addWidget(styleButton, 0, Qt::AlignTop); @@ -298,10 +317,15 @@ int main(int argc, char **argv) vLayout2->addWidget(fontSizeSlider); vLayout2->addWidget(new QLabel(QStringLiteral("Adjust aspect ratio"))); vLayout2->addWidget(aspectRatioSlider, 1, Qt::AlignTop); + vLayout2->addWidget(optimizationStaticCB); + vLayout2->addWidget(orthoCB); + vLayout2->addWidget(polarCB); vLayout2->addWidget(axisTitlesVisibleCB); vLayout2->addWidget(axisTitlesFixedCB); vLayout2->addWidget(new QLabel(QStringLiteral("Axis label rotation"))); - vLayout2->addWidget(axisLabelRotationSlider, 1, Qt::AlignTop); + vLayout2->addWidget(axisLabelRotationSlider); + vLayout2->addWidget(new QLabel(QStringLiteral("Radial label offset"))); + vLayout2->addWidget(radialLabelSlider, 1, Qt::AlignTop); ScatterDataModifier *modifier = new ScatterDataModifier(chart); @@ -389,6 +413,12 @@ int main(int argc, char **argv) &ScatterDataModifier::setMaxY); QObject::connect(maxSliderZ, &QSlider::valueChanged, modifier, &ScatterDataModifier::setMaxZ); + QObject::connect(optimizationStaticCB, &QCheckBox::stateChanged, modifier, + &ScatterDataModifier::toggleStatic); + QObject::connect(orthoCB, &QCheckBox::stateChanged, modifier, + &ScatterDataModifier::toggleOrtho); + QObject::connect(polarCB, &QCheckBox::stateChanged, modifier, + &ScatterDataModifier::togglePolar); QObject::connect(axisTitlesVisibleCB, &QCheckBox::stateChanged, modifier, &ScatterDataModifier::toggleAxisTitleVisibility); QObject::connect(axisTitlesFixedCB, &QCheckBox::stateChanged, modifier, @@ -397,6 +427,8 @@ int main(int argc, char **argv) &ScatterDataModifier::changeLabelRotation); QObject::connect(aspectRatioSlider, &QSlider::valueChanged, modifier, &ScatterDataModifier::setAspectRatio); + QObject::connect(radialLabelSlider, &QSlider::valueChanged, modifier, + &ScatterDataModifier::changeRadialLabelOffset); modifier->setFpsLabel(fpsLabel); diff --git a/tests/scattertest/scatterchart.cpp b/tests/scattertest/scatterchart.cpp index c00c526a..5a0a5976 100644 --- a/tests/scattertest/scatterchart.cpp +++ b/tests/scattertest/scatterchart.cpp @@ -484,9 +484,9 @@ void ScatterDataModifier::addData() m_chart->axisX()->setRange(-50.0f, 50.0f); m_chart->axisY()->setRange(-1.0f, 1.2f); m_chart->axisZ()->setRange(-50.0f, 50.0f); - m_chart->axisX()->setSegmentCount(6); + m_chart->axisX()->setSegmentCount(5); m_chart->axisY()->setSegmentCount(4); - m_chart->axisZ()->setSegmentCount(9); + m_chart->axisZ()->setSegmentCount(10); m_chart->axisX()->setSubSegmentCount(2); m_chart->axisY()->setSubSegmentCount(3); m_chart->axisZ()->setSubSegmentCount(1); @@ -934,6 +934,11 @@ void ScatterDataModifier::changeLabelRotation(int rotation) m_chart->axisZ()->setLabelAutoRotation(float(rotation)); } +void ScatterDataModifier::changeRadialLabelOffset(int offset) +{ + m_chart->setRadialLabelOffset(float(offset) / 100.0f); +} + void ScatterDataModifier::toggleAxisTitleVisibility(bool enabled) { m_chart->axisX()->setTitleVisible(enabled); @@ -970,6 +975,24 @@ void ScatterDataModifier::renderToImage() } } +void ScatterDataModifier::togglePolar(bool enable) +{ + m_chart->setPolar(enable); +} + +void ScatterDataModifier::toggleStatic(bool enable) +{ + if (enable) + m_chart->setOptimizationHints(QAbstract3DGraph::OptimizationStatic); + else + m_chart->setOptimizationHints(QAbstract3DGraph::OptimizationDefault); +} + +void ScatterDataModifier::toggleOrtho(bool enable) +{ + m_chart->setOrthoProjection(enable); +} + void ScatterDataModifier::changeShadowQuality(int quality) { QAbstract3DGraph::ShadowQuality sq = QAbstract3DGraph::ShadowQuality(quality); diff --git a/tests/scattertest/scatterchart.h b/tests/scattertest/scatterchart.h index 97c3b1f9..e2e979f3 100644 --- a/tests/scattertest/scatterchart.h +++ b/tests/scattertest/scatterchart.h @@ -90,9 +90,13 @@ public slots: void handleAxisZChanged(QValue3DAxis *axis); void handleFpsChange(qreal fps); void changeLabelRotation(int rotation); + void changeRadialLabelOffset(int offset); void toggleAxisTitleVisibility(bool enabled); void toggleAxisTitleFixed(bool enabled); void renderToImage(); + void togglePolar(bool enable); + void toggleStatic(bool enable); + void toggleOrtho(bool enable); signals: void shadowQualityChanged(int quality); -- cgit v1.2.3 From b10ef3ea924efe8127dfc1e93d8094291daf365e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomi=20Korpip=C3=A4=C3=A4?= Date: Fri, 27 Jun 2014 07:14:29 +0300 Subject: Wrong term fixed in example MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I34b5efc1253e0d9ab043568d2f64aaa806b167bb Reviewed-by: Tomi Korpipää --- examples/datavisualization/qmlspectrogram/qml/qmlspectrogram/main.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/datavisualization/qmlspectrogram/qml/qmlspectrogram/main.qml b/examples/datavisualization/qmlspectrogram/qml/qmlspectrogram/main.qml index a1321c14..97e47b96 100644 --- a/examples/datavisualization/qmlspectrogram/qml/qmlspectrogram/main.qml +++ b/examples/datavisualization/qmlspectrogram/qml/qmlspectrogram/main.qml @@ -154,7 +154,7 @@ Window { xAxis.labelAutoRotation = 30 yAxis.labelAutoRotation = 30 zAxis.labelAutoRotation = 30 - text = "Switch to orthogonal" + text = "Switch to orthographic" } else { surfaceGraph.orthoProjection = true; surfaceGraph.scene.activeCamera.cameraPreset = Camera3D.CameraPresetDirectlyAbove -- cgit v1.2.3 From f642e1c6eb37e50d52e2abf9289c98d953d42868 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomi=20Korpip=C3=A4=C3=A4?= Date: Fri, 27 Jun 2014 07:15:57 +0300 Subject: Removed unnecessary texture enablings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: Ia35d2b73a76372c7e92670bb2c379ebc2f387b4a Reviewed-by: Tomi Korpipää --- src/datavisualization/engine/scatter3drenderer.cpp | 6 ------ src/datavisualization/engine/selectionpointer.cpp | 6 ------ src/datavisualization/engine/surface3drenderer.cpp | 14 ++------------ 3 files changed, 2 insertions(+), 24 deletions(-) diff --git a/src/datavisualization/engine/scatter3drenderer.cpp b/src/datavisualization/engine/scatter3drenderer.cpp index 2935d02d..31c3973b 100644 --- a/src/datavisualization/engine/scatter3drenderer.cpp +++ b/src/datavisualization/engine/scatter3drenderer.cpp @@ -686,7 +686,6 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) previousMeshColorStyle = Q3DTheme::ColorStyleRangeGradient; m_dotGradientShader->setUniformValue(m_dotGradientShader->gradientHeight(), 0.0f); } - glEnable(GL_TEXTURE_2D); } else { dotShader = pointSelectionShader; previousDrawingPoints = true; @@ -1045,9 +1044,6 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) } } - // Disable textures - glDisable(GL_TEXTURE_2D); - // Draw grid lines QVector3D gridLineScaleX(m_scaleXWithBackground, gridLineWidth, gridLineWidth); QVector3D gridLineScaleZ(gridLineWidth, gridLineWidth, m_scaleZWithBackground); @@ -1429,7 +1425,6 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) glEnable(GL_DEPTH_TEST); } - glDisable(GL_TEXTURE_2D); glDisable(GL_BLEND); // Release shader @@ -1452,7 +1447,6 @@ void Scatter3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa shader = m_labelShader; shader->bind(); - glEnable(GL_TEXTURE_2D); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); } diff --git a/src/datavisualization/engine/selectionpointer.cpp b/src/datavisualization/engine/selectionpointer.cpp index 183d3f8e..c40034ba 100644 --- a/src/datavisualization/engine/selectionpointer.cpp +++ b/src/datavisualization/engine/selectionpointer.cpp @@ -122,9 +122,6 @@ void SelectionPointer::render(GLuint defaultFboHandle, bool useOrtho) MVPMatrix = projectionMatrix * viewMatrix * modelMatrix; - // Enable texturing - glEnable(GL_TEXTURE_2D); - QVector3D lightPos = m_cachedScene->activeLight()->position(); // @@ -186,9 +183,6 @@ void SelectionPointer::render(GLuint defaultFboHandle, bool useOrtho) // Release shader glUseProgram(0); - // Disable textures - glDisable(GL_TEXTURE_2D); - // Disable transparency glDisable(GL_BLEND); diff --git a/src/datavisualization/engine/surface3drenderer.cpp b/src/datavisualization/engine/surface3drenderer.cpp index 1558b636..cc1ac1ae 100644 --- a/src/datavisualization/engine/surface3drenderer.cpp +++ b/src/datavisualization/engine/surface3drenderer.cpp @@ -832,9 +832,6 @@ void Surface3DRenderer::drawSlicedScene() } } - // Disable textures - glDisable(GL_TEXTURE_2D); - glEnable(GL_CULL_FACE); glCullFace(GL_BACK); @@ -927,7 +924,6 @@ void Surface3DRenderer::drawSlicedScene() // Draw labels m_labelShader->bind(); - glEnable(GL_TEXTURE_2D); glDisable(GL_DEPTH_TEST); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); @@ -1000,7 +996,6 @@ void Surface3DRenderer::drawSlicedScene() m_cachedSelectionMode, m_labelShader, m_labelObj, activeCamera, false, false, Drawer::LabelMid, Qt::AlignBottom); - glDisable(GL_TEXTURE_2D); glEnable(GL_DEPTH_TEST); glDisable(GL_BLEND); @@ -1200,9 +1195,6 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) glCullFace(GL_BACK); } #endif - // Enable texturing - glEnable(GL_TEXTURE_2D); - // Draw selection buffer if (!m_cachedIsSlicingActivated && (!m_renderCacheList.isEmpty() || !m_customRenderCache.isEmpty()) @@ -1837,7 +1829,7 @@ void Surface3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa } else { shader = m_labelShader; shader->bind(); - glEnable(GL_TEXTURE_2D); + glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); } @@ -2262,10 +2254,8 @@ void Surface3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa } glDisable(GL_POLYGON_OFFSET_FILL); - if (!drawSelection) { - glDisable(GL_TEXTURE_2D); + if (!drawSelection) glDisable(GL_BLEND); - } } void Surface3DRenderer::updateSelectionMode(QAbstract3DGraph::SelectionFlags mode) -- cgit v1.2.3 From a0196afc135d63f69aab5626424cc2e21ccac611 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Thu, 26 Jun 2014 15:03:14 +0300 Subject: Fix polar axis title positioning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Task-number: QTRD-3184 Change-Id: I366f41b928e06931784c6ff74e9b6b8a52414e3f Reviewed-by: Tomi Korpipää --- .../qmlspectrogram/qml/qmlspectrogram/main.qml | 16 +++++++++++---- .../engine/abstract3drenderer.cpp | 23 +++++++++++++++++++--- .../engine/abstract3drenderer_p.h | 3 ++- src/datavisualization/engine/scatter3drenderer.cpp | 23 ++++++++++++++++++++-- src/datavisualization/engine/surface3drenderer.cpp | 23 ++++++++++++++++++++-- 5 files changed, 76 insertions(+), 12 deletions(-) diff --git a/examples/datavisualization/qmlspectrogram/qml/qmlspectrogram/main.qml b/examples/datavisualization/qmlspectrogram/qml/qmlspectrogram/main.qml index 97e47b96..66fa807f 100644 --- a/examples/datavisualization/qmlspectrogram/qml/qmlspectrogram/main.qml +++ b/examples/datavisualization/qmlspectrogram/qml/qmlspectrogram/main.qml @@ -80,6 +80,17 @@ Window { titleFixed: false } + Theme3D { + id: customTheme + type: Theme3D.ThemeQt + // Don't show specular spotlight as we don't want it to distort the colors + lightStrength: 0.0 + ambientLightStrength: 0.9 + backgroundEnabled: false + font.family: "Lucida Handwriting" + font.pointSize: 25 + } + Surface3D { id: surfaceGraph width: surfaceView.width @@ -92,10 +103,7 @@ Window { axisY: yAxis axisZ: zAxis - // Don't show specular spotlight as we don't want it to distort the colors - theme.lightStrength: 0.0 - theme.ambientLightStrength: 0.9 - theme.backgroundEnabled: false + theme: customTheme orthoProjection: true flipHorizontalGrid: true diff --git a/src/datavisualization/engine/abstract3drenderer.cpp b/src/datavisualization/engine/abstract3drenderer.cpp index f520e279..026795e9 100644 --- a/src/datavisualization/engine/abstract3drenderer.cpp +++ b/src/datavisualization/engine/abstract3drenderer.cpp @@ -74,7 +74,8 @@ Abstract3DRenderer::Abstract3DRenderer(Abstract3DController *controller) m_xRightAngleRotationNeg(QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, -90.0f)), m_yRightAngleRotationNeg(QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, -90.0f)), m_zRightAngleRotationNeg(QQuaternion::fromAxisAndAngle(0.0f, 0.0f, 1.0f, -90.0f)), - m_xFlipRotation(QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, -180.0f)) + m_xFlipRotation(QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, -180.0f)), + m_zFlipRotation(QQuaternion::fromAxisAndAngle(0.0f, 0.0f, 1.0f, -180.0f)) { QObject::connect(m_drawer, &Drawer::drawerChanged, this, &Abstract3DRenderer::updateTextures); QObject::connect(this, &Abstract3DRenderer::needRender, controller, @@ -662,10 +663,15 @@ void Abstract3DRenderer::drawAxisTitleX(const QVector3D &labelRotation, float labelsMaxWidth, const QMatrix4x4 &viewMatrix, const QMatrix4x4 &projectionMatrix, - ShaderHelper *shader) + ShaderHelper *shader, + bool radial) { float scaleFactor = m_drawer->scaledFontSize() / m_axisCacheX.titleItem().size().height(); - float titleOffset = 2.0f * (labelMargin + (labelsMaxWidth * scaleFactor)); + float titleOffset; + if (radial) + titleOffset = -2.0f * (labelMargin + m_drawer->scaledFontSize()); + else + titleOffset = 2.0f * (labelMargin + (labelsMaxWidth * scaleFactor)); float zRotation = 0.0f; float yRotation = 0.0f; float xRotation = -90.0f + labelRotation.z(); @@ -712,6 +718,17 @@ void Abstract3DRenderer::drawAxisTitleX(const QVector3D &labelRotation, } } + if (radial) { + if (m_zFlipped) { + titleOffset = -titleOffset; + } else { + if (m_yFlippedForGrid) + alignment = Qt::AlignTop; + else + alignment = Qt::AlignBottom; + } + } + if (offsetRotation == 180.0f || offsetRotation == -180.0f) offsetRotation = 0.0f; QQuaternion offsetRotator = QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, offsetRotation); diff --git a/src/datavisualization/engine/abstract3drenderer_p.h b/src/datavisualization/engine/abstract3drenderer_p.h index ed26a4ec..254d61ca 100644 --- a/src/datavisualization/engine/abstract3drenderer_p.h +++ b/src/datavisualization/engine/abstract3drenderer_p.h @@ -190,7 +190,7 @@ protected: const QQuaternion &totalRotation, AbstractRenderItem &dummyItem, const Q3DCamera *activeCamera, float labelsMaxWidth, const QMatrix4x4 &viewMatrix, const QMatrix4x4 &projectionMatrix, - ShaderHelper *shader); + ShaderHelper *shader, bool radial = false); void drawAxisTitleZ(const QVector3D &labelRotation, const QVector3D &labelTrans, const QQuaternion &totalRotation, AbstractRenderItem &dummyItem, const Q3DCamera *activeCamera, float labelsMaxWidth, @@ -264,6 +264,7 @@ protected: QQuaternion m_yRightAngleRotationNeg; QQuaternion m_zRightAngleRotationNeg; QQuaternion m_xFlipRotation; + QQuaternion m_zFlipRotation; private: friend class Abstract3DController; diff --git a/src/datavisualization/engine/scatter3drenderer.cpp b/src/datavisualization/engine/scatter3drenderer.cpp index 31c3973b..86faca96 100644 --- a/src/datavisualization/engine/scatter3drenderer.cpp +++ b/src/datavisualization/engine/scatter3drenderer.cpp @@ -1580,7 +1580,14 @@ void Scatter3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa labelsMaxWidth = qMax(labelsMaxWidth, float(axisLabelItem.size().width())); } if (!drawSelection && m_axisCacheZ.isTitleVisible()) { - labelTrans.setZ(0.0f); + if (m_polarGraph) { + float titleZ = -m_graphAspectRatio / 2.0f; + if (m_zFlipped) + titleZ = -titleZ; + labelTrans.setZ(titleZ); + } else { + labelTrans.setZ(0.0f); + } drawAxisTitleZ(labelRotation, labelTrans, totalRotation, m_dummyRenderItem, activeCamera, labelsMaxWidth, viewMatrix, projectionMatrix, shader); } @@ -1746,8 +1753,20 @@ void Scatter3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa } if (!drawSelection && m_axisCacheX.isTitleVisible()) { labelTrans.setX(0.0f); + bool radial = false; + if (m_polarGraph) { + if (m_xFlipped == m_zFlipped) + totalRotation *= m_zRightAngleRotation; + else + totalRotation *= m_zRightAngleRotationNeg; + if (m_yFlippedForGrid) + totalRotation *= QQuaternion::fromAxisAndAngle(0.0f, 0.0f, 1.0f, -180.0f); + labelTrans.setZ(-m_graphAspectRatio); + radial = true; + } drawAxisTitleX(labelRotation, labelTrans, totalRotation, m_dummyRenderItem, - activeCamera, labelsMaxWidth, viewMatrix, projectionMatrix, shader); + activeCamera, labelsMaxWidth, viewMatrix, projectionMatrix, shader, + radial); } } diff --git a/src/datavisualization/engine/surface3drenderer.cpp b/src/datavisualization/engine/surface3drenderer.cpp index cc1ac1ae..91aeb77f 100644 --- a/src/datavisualization/engine/surface3drenderer.cpp +++ b/src/datavisualization/engine/surface3drenderer.cpp @@ -1969,7 +1969,14 @@ void Surface3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa labelsMaxWidth = qMax(labelsMaxWidth, float(axisLabelItem.size().width())); } if (!drawSelection && m_axisCacheZ.isTitleVisible()) { - labelTrans.setZ(0.0f); + if (m_polarGraph) { + float titleZ = -m_graphAspectRatio / 2.0f; + if (m_zFlipped) + titleZ = -titleZ; + labelTrans.setZ(titleZ); + } else { + labelTrans.setZ(0.0f); + } drawAxisTitleZ(labelRotation, labelTrans, totalRotation, m_dummyRenderItem, activeCamera, labelsMaxWidth, viewMatrix, projectionMatrix, shader); } @@ -2139,8 +2146,20 @@ void Surface3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa } if (!drawSelection && m_axisCacheX.isTitleVisible()) { labelTrans.setX(0.0f); + bool radial = false; + if (m_polarGraph) { + if (m_xFlipped == m_zFlipped) + totalRotation *= m_zRightAngleRotation; + else + totalRotation *= m_zRightAngleRotationNeg; + if (m_yFlippedForGrid) + totalRotation *= QQuaternion::fromAxisAndAngle(0.0f, 0.0f, 1.0f, -180.0f); + labelTrans.setZ(-m_graphAspectRatio); + radial = true; + } drawAxisTitleX(labelRotation, labelTrans, totalRotation, m_dummyRenderItem, - activeCamera, labelsMaxWidth, viewMatrix, projectionMatrix, shader); + activeCamera, labelsMaxWidth, viewMatrix, projectionMatrix, shader, + radial); } } // Y Labels -- cgit v1.2.3 From 600aa5708e9ee033c98561d6e44aa4e9901b63b1 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Fri, 27 Jun 2014 12:58:12 +0300 Subject: Adjust background margins based on angular labels and title Also separated the vertical and horizontal background margins. Task-number: QTRD-3184 Change-Id: I988217d4df7749585dd85b5ea8f3f50254dca6ad Reviewed-by: Mika Salmela --- .../qmlspectrogram/qml/qmlspectrogram/main.qml | 36 +++++----- .../engine/abstract3drenderer.cpp | 39 +++++++++- .../engine/abstract3drenderer_p.h | 7 +- src/datavisualization/engine/axisrendercache.cpp | 4 +- src/datavisualization/engine/axisrendercache_p.h | 1 - src/datavisualization/engine/bars3drenderer.cpp | 2 + src/datavisualization/engine/scatter3drenderer.cpp | 83 +++++++++++++++------- src/datavisualization/engine/scatter3drenderer_p.h | 8 ++- src/datavisualization/engine/surface3drenderer.cpp | 82 ++++++++++++++------- src/datavisualization/engine/surface3drenderer_p.h | 15 ++-- tests/scattertest/main.cpp | 2 +- 11 files changed, 198 insertions(+), 81 deletions(-) diff --git a/examples/datavisualization/qmlspectrogram/qml/qmlspectrogram/main.qml b/examples/datavisualization/qmlspectrogram/qml/qmlspectrogram/main.qml index 66fa807f..f7e93827 100644 --- a/examples/datavisualization/qmlspectrogram/qml/qmlspectrogram/main.qml +++ b/examples/datavisualization/qmlspectrogram/qml/qmlspectrogram/main.qml @@ -99,6 +99,7 @@ Window { shadowQuality: AbstractGraph3D.ShadowQualityNone selectionMode: AbstractGraph3D.SelectionSlice | AbstractGraph3D.SelectionItemAndRow scene.activeCamera.cameraPreset: Camera3D.CameraPresetDirectlyAbove + scene.activeCamera.zoomLevel: 85 axisX: xAxis axisY: yAxis axisZ: zAxis @@ -166,6 +167,7 @@ Window { } else { surfaceGraph.orthoProjection = true; surfaceGraph.scene.activeCamera.cameraPreset = Camera3D.CameraPresetDirectlyAbove + surfaceSeries.drawMode &= ~Surface3DSeries.DrawWireframe; xAxis.labelAutoRotation = 0 yAxis.labelAutoRotation = 0 zAxis.labelAutoRotation = 0 @@ -174,20 +176,6 @@ Window { } } - NewButton { - id: surfaceGridToggle - Layout.fillWidth: true - Layout.fillHeight: true - text: "Toggle surface grid" - onClicked: { - if (surfaceSeries.drawMode & Surface3DSeries.DrawWireframe) { - surfaceSeries.drawMode &= ~Surface3DSeries.DrawWireframe; - } else { - surfaceSeries.drawMode |= Surface3DSeries.DrawWireframe; - } - } - } - NewButton { id: flipGridToggle Layout.fillWidth: true @@ -211,13 +199,29 @@ Window { text: "Toggle radial label position" visible: surfaceGraph.polar onClicked: { - if (surfaceGraph.radialLabelOffset > 1.0) { + if (surfaceGraph.radialLabelOffset >= 1.0) { surfaceGraph.radialLabelOffset = 0.01 } else { - surfaceGraph.radialLabelOffset = 1.06 + surfaceGraph.radialLabelOffset = 1.0 } } } + + NewButton { + id: surfaceGridToggle + Layout.fillWidth: true + Layout.fillHeight: true + text: "Toggle surface grid" + visible: !surfaceGraph.orthoProjection + onClicked: { + if (surfaceSeries.drawMode & Surface3DSeries.DrawWireframe) { + surfaceSeries.drawMode &= ~Surface3DSeries.DrawWireframe; + } else { + surfaceSeries.drawMode |= Surface3DSeries.DrawWireframe; + } + } + } + } Rectangle { diff --git a/src/datavisualization/engine/abstract3drenderer.cpp b/src/datavisualization/engine/abstract3drenderer.cpp index 026795e9..19fdf53b 100644 --- a/src/datavisualization/engine/abstract3drenderer.cpp +++ b/src/datavisualization/engine/abstract3drenderer.cpp @@ -75,7 +75,9 @@ Abstract3DRenderer::Abstract3DRenderer(Abstract3DController *controller) m_yRightAngleRotationNeg(QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, -90.0f)), m_zRightAngleRotationNeg(QQuaternion::fromAxisAndAngle(0.0f, 0.0f, 1.0f, -90.0f)), m_xFlipRotation(QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, -180.0f)), - m_zFlipRotation(QQuaternion::fromAxisAndAngle(0.0f, 0.0f, 1.0f, -180.0f)) + m_zFlipRotation(QQuaternion::fromAxisAndAngle(0.0f, 0.0f, 1.0f, -180.0f)), + m_vBackgroundMargin(0.1f), + m_hBackgroundMargin(0.1f) { QObject::connect(m_drawer, &Drawer::drawerChanged, this, &Abstract3DRenderer::updateTextures); QObject::connect(this, &Abstract3DRenderer::needRender, controller, @@ -247,6 +249,13 @@ void Abstract3DRenderer::updateScene(Q3DScene *scene) } } +void Abstract3DRenderer::updateTextures() +{ + m_axisCacheX.updateTextures(); + m_axisCacheY.updateTextures(); + m_axisCacheZ.updateTextures(); +} + void Abstract3DRenderer::reInitShaders() { #if !defined(QT_OPENGL_ES_2) @@ -1302,4 +1311,32 @@ void Abstract3DRenderer::drawAngularGrid(ShaderHelper *shader, float yFloorLineP } } +float Abstract3DRenderer::calculatePolarBackgroundMargin() +{ + // Check each extents of each angular label + // Calculate angular position + QVector &labelPositions = m_axisCacheX.formatter()->labelPositions(); + float actualLabelHeight = m_drawer->scaledFontSize() * 2.0f; // All labels are same height + float maxNeededMargin = 0.0f; + + // Axis title needs to be accounted for + if (m_axisCacheX.isTitleVisible()) + maxNeededMargin = 2.0f * actualLabelHeight + 3.0f * labelMargin; + + for (int label = 0; label < labelPositions.size(); label++) { + QSize labelSize = m_axisCacheX.labelItems().at(label)->size(); + float actualLabelWidth = actualLabelHeight / labelSize.height() * labelSize.width(); + float labelPosition = labelPositions.at(label); + qreal angle = labelPosition * M_PI * 2.0; + float x = qAbs((m_graphAspectRatio + labelMargin) * float(qSin(angle))) + + actualLabelWidth - m_graphAspectRatio + labelMargin; + float z = qAbs(-(m_graphAspectRatio + labelMargin) * float(qCos(angle))) + + actualLabelHeight - m_graphAspectRatio + labelMargin; + float neededMargin = qMax(x, z); + maxNeededMargin = qMax(maxNeededMargin, neededMargin); + } + + return maxNeededMargin; +} + QT_END_NAMESPACE_DATAVISUALIZATION diff --git a/src/datavisualization/engine/abstract3drenderer_p.h b/src/datavisualization/engine/abstract3drenderer_p.h index 254d61ca..0c24c41e 100644 --- a/src/datavisualization/engine/abstract3drenderer_p.h +++ b/src/datavisualization/engine/abstract3drenderer_p.h @@ -79,7 +79,7 @@ public: virtual void updateSelectionMode(QAbstract3DGraph::SelectionFlags newMode); virtual void updateOptimizationHint(QAbstract3DGraph::OptimizationHints hint); virtual void updateScene(Q3DScene *scene); - virtual void updateTextures() = 0; + virtual void updateTextures(); virtual void initSelectionBuffer() = 0; virtual void updateSelectionState(SelectionState state); virtual void updateInputPosition(const QPoint &position); @@ -205,6 +205,8 @@ protected: void drawAngularGrid(ShaderHelper *shader, float yFloorLinePos, const QMatrix4x4 &projectionViewMatrix, const QMatrix4x4 &depthMatrix); + float calculatePolarBackgroundMargin(); + bool m_hasNegativeValues; Q3DTheme *m_cachedTheme; Drawer *m_drawer; @@ -266,6 +268,9 @@ protected: QQuaternion m_xFlipRotation; QQuaternion m_zFlipRotation; + float m_vBackgroundMargin; + float m_hBackgroundMargin; + private: friend class Abstract3DController; }; diff --git a/src/datavisualization/engine/axisrendercache.cpp b/src/datavisualization/engine/axisrendercache.cpp index 6fdb84c6..2448483e 100644 --- a/src/datavisualization/engine/axisrendercache.cpp +++ b/src/datavisualization/engine/axisrendercache.cpp @@ -54,10 +54,8 @@ void AxisRenderCache::setDrawer(Drawer *drawer) { m_drawer = drawer; m_font = m_drawer->font(); - if (m_drawer) { - QObject::connect(m_drawer, &Drawer::drawerChanged, this, &AxisRenderCache::updateTextures); + if (m_drawer) updateTextures(); - } } void AxisRenderCache::setType(QAbstract3DAxis::AxisType type) diff --git a/src/datavisualization/engine/axisrendercache_p.h b/src/datavisualization/engine/axisrendercache_p.h index 90321740..9fb04837 100644 --- a/src/datavisualization/engine/axisrendercache_p.h +++ b/src/datavisualization/engine/axisrendercache_p.h @@ -107,7 +107,6 @@ public: inline bool isTitleFixed() const { return m_titleFixed; } inline void setTitleFixed(bool fixed) { m_titleFixed = fixed; } -public slots: void updateTextures(); private: diff --git a/src/datavisualization/engine/bars3drenderer.cpp b/src/datavisualization/engine/bars3drenderer.cpp index 70310c23..0b4d5b25 100644 --- a/src/datavisualization/engine/bars3drenderer.cpp +++ b/src/datavisualization/engine/bars3drenderer.cpp @@ -2553,6 +2553,8 @@ void Bars3DRenderer::loadBackgroundMesh() void Bars3DRenderer::updateTextures() { + Abstract3DRenderer::updateTextures(); + // Drawer has changed; this flag needs to be checked when checking if we need to update labels m_updateLabels = true; } diff --git a/src/datavisualization/engine/scatter3drenderer.cpp b/src/datavisualization/engine/scatter3drenderer.cpp index 86faca96..075f252f 100644 --- a/src/datavisualization/engine/scatter3drenderer.cpp +++ b/src/datavisualization/engine/scatter3drenderer.cpp @@ -63,13 +63,13 @@ Scatter3DRenderer::Scatter3DRenderer(Scatter3DController *controller) m_scaleX(0.0f), m_scaleZ(0.0f), m_scaleXWithBackground(0.0f), + m_scaleYWithBackground(0.0f), m_scaleZWithBackground(0.0f), m_selectedItemIndex(Scatter3DController::invalidSelectionIndex()), m_selectedSeriesCache(0), m_oldSelectedSeriesCache(0), m_dotSizeScale(1.0f), m_hasHeightAdjustmentChanged(true), - m_backgroundMargin(defaultMaxSize), m_maxItemSize(0.0f), m_clickedIndex(Scatter3DController::invalidSelectionIndex()), m_havePointSeries(false), @@ -162,7 +162,8 @@ void Scatter3DRenderer::updateData() } if (totalDataSize) { - m_dotSizeScale = GLfloat(qBound(defaultMinSize, 2.0f / float(qSqrt(qreal(totalDataSize))), + m_dotSizeScale = GLfloat(qBound(defaultMinSize, + 2.0f / float(qSqrt(qreal(totalDataSize))), defaultMaxSize)); } @@ -246,10 +247,6 @@ void Scatter3DRenderer::updateSeries(const QList &seriesLis } } m_maxItemSize = maxItemSize; - if (maxItemSize > defaultMaxSize) - m_backgroundMargin = maxItemSize / itemScaler; - else - m_backgroundMargin = defaultMaxSize; calculateSceneScalingFactors(); if (noSelection) { @@ -302,6 +299,26 @@ void Scatter3DRenderer::updateScene(Q3DScene *scene) Abstract3DRenderer::updateScene(scene); } +void Scatter3DRenderer::updateAxisLabels(QAbstract3DAxis::AxisOrientation orientation, + const QStringList &labels) +{ + Abstract3DRenderer::updateAxisLabels(orientation, labels); + + // Angular axis label dimensions affect the chart dimensions + if (m_polarGraph && orientation == QAbstract3DAxis::AxisOrientationX) + calculateSceneScalingFactors(); +} + +void Scatter3DRenderer::updateAxisTitleVisibility(QAbstract3DAxis::AxisOrientation orientation, + bool visible) +{ + Abstract3DRenderer::updateAxisTitleVisibility(orientation, visible); + + // Angular axis title existence affects the chart dimensions + if (m_polarGraph && orientation == QAbstract3DAxis::AxisOrientationX) + calculateSceneScalingFactors(); +} + void Scatter3DRenderer::resetClickedStatus() { m_clickedIndex = Scatter3DController::invalidSelectionIndex(); @@ -989,7 +1006,7 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) QMatrix4x4 MVPMatrix; QMatrix4x4 itModelMatrix; - QVector3D bgScale(m_scaleXWithBackground, 1.0f + m_backgroundMargin, + QVector3D bgScale(m_scaleXWithBackground, m_scaleYWithBackground, m_scaleZWithBackground); modelMatrix.scale(bgScale); // If we're viewing from below, background object must be flipped @@ -1047,7 +1064,7 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) // Draw grid lines QVector3D gridLineScaleX(m_scaleXWithBackground, gridLineWidth, gridLineWidth); QVector3D gridLineScaleZ(gridLineWidth, gridLineWidth, m_scaleZWithBackground); - QVector3D gridLineScaleY(gridLineWidth, 1.0f + m_backgroundMargin, gridLineWidth); + QVector3D gridLineScaleY(gridLineWidth, m_scaleYWithBackground, gridLineWidth); if (m_cachedTheme->isGridEnabled()) { #if !(defined QT_OPENGL_ES_2) @@ -1093,7 +1110,7 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) else lineXRotation = m_xRightAngleRotationNeg; - GLfloat yFloorLinePosition = -1.0f - m_backgroundMargin + gridLineOffset; + GLfloat yFloorLinePosition = -m_scaleYWithBackground + gridLineOffset; if (m_yFlippedForGrid) yFloorLinePosition = -yFloorLinePosition; @@ -1467,7 +1484,7 @@ void Scatter3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa if (m_axisCacheZ.segmentCount() > 0) { int labelCount = m_axisCacheZ.labelCount(); float labelXTrans = m_scaleXWithBackground + labelMargin; - float labelYTrans = -1.0f - m_backgroundMargin; + float labelYTrans = -m_scaleYWithBackground; if (m_polarGraph) { labelXTrans *= m_radialLabelOffset; // YTrans up only if over background @@ -1602,7 +1619,7 @@ void Scatter3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa fractionCamX = activeCamera->xRotation() * labelAngleFraction; int labelCount = m_axisCacheX.labelCount(); float labelZTrans = 0.0f; - float labelYTrans = -1.0f - m_backgroundMargin; + float labelYTrans = -m_scaleYWithBackground; if (m_polarGraph) labelYTrans += gridLineOffset + gridLineWidth; else @@ -1698,10 +1715,10 @@ void Scatter3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa } float offsetValue = 0.0f; bool showLastLabel = false; - QVector &gridPositions = m_axisCacheX.formatter()->gridPositions(); - int lastGridPosIndex = gridPositions.size() - 1; - if (gridPositions.size() - && (gridPositions.at(lastGridPosIndex) != 1.0f || gridPositions.at(0) != 0.0f)) { + QVector &labelPositions = m_axisCacheX.formatter()->labelPositions(); + int lastLabelPosIndex = labelPositions.size() - 1; + if (labelPositions.size() + && (labelPositions.at(lastLabelPosIndex) != 1.0f || labelPositions.at(0) != 0.0f)) { // Avoid overlapping first and last label if they would get on same position showLastLabel = true; } @@ -1711,24 +1728,24 @@ void Scatter3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa // Draw the label here if (m_polarGraph) { // Calculate angular position - if (label == lastGridPosIndex && !showLastLabel) + if (label == lastLabelPosIndex && !showLastLabel) continue; - float gridPosition = gridPositions.at(label); - qreal angle = gridPosition * M_PI * 2.0; - labelTrans.setX((m_graphAspectRatio + labelMargin)* float(qSin(angle))); + float labelPosition = labelPositions.at(label); + qreal angle = labelPosition * M_PI * 2.0; + labelTrans.setX((m_graphAspectRatio + labelMargin) * float(qSin(angle))); labelTrans.setZ(-(m_graphAspectRatio + labelMargin) * float(qCos(angle))); // Alignment depends on label angular position, as well as flips Qt::AlignmentFlag vAlignment = Qt::AlignCenter; Qt::AlignmentFlag hAlignment = Qt::AlignCenter; const float centerMargin = 0.005f; - if (gridPosition < 0.25f - centerMargin || gridPosition > 0.75f + centerMargin) + if (labelPosition < 0.25f - centerMargin || labelPosition > 0.75f + centerMargin) vAlignment = m_zFlipped ? Qt::AlignTop : Qt::AlignBottom; - else if (gridPosition > 0.25f + centerMargin && gridPosition < 0.75f - centerMargin) + else if (labelPosition > 0.25f + centerMargin && labelPosition < 0.75f - centerMargin) vAlignment = m_zFlipped ? Qt::AlignBottom : Qt::AlignTop; - if (gridPosition < 0.50f - centerMargin && gridPosition > centerMargin) + if (labelPosition < 0.50f - centerMargin && labelPosition > centerMargin) hAlignment = m_zFlipped ? Qt::AlignRight : Qt::AlignLeft; - else if (gridPosition < 1.0f - centerMargin && gridPosition > 0.5f + centerMargin) + else if (labelPosition < 1.0f - centerMargin && labelPosition > 0.5f + centerMargin) hAlignment = m_zFlipped ? Qt::AlignLeft : Qt::AlignRight; if (m_yFlippedForGrid && vAlignment != Qt::AlignCenter) vAlignment = (vAlignment == Qt::AlignTop) ? Qt::AlignBottom : Qt::AlignTop; @@ -1959,8 +1976,13 @@ void Scatter3DRenderer::loadBackgroundMesh() void Scatter3DRenderer::updateTextures() { + Abstract3DRenderer::updateTextures(); + // Drawer has changed; this flag needs to be checked when checking if we need to update labels m_updateLabels = true; + + if (m_polarGraph) + calculateSceneScalingFactors(); } void Scatter3DRenderer::fixMeshFileName(QString &fileName, QAbstract3DSeries::Mesh mesh) @@ -1992,6 +2014,16 @@ void Scatter3DRenderer::calculateTranslation(ScatterRenderItem &item) void Scatter3DRenderer::calculateSceneScalingFactors() { + if (m_maxItemSize > defaultMaxSize) + m_hBackgroundMargin = m_maxItemSize / itemScaler; + else + m_hBackgroundMargin = defaultMaxSize; + m_vBackgroundMargin = m_hBackgroundMargin; + if (m_polarGraph) { + float polarMargin = calculatePolarBackgroundMargin(); + m_hBackgroundMargin = qMax(m_hBackgroundMargin, polarMargin); + } + m_heightNormalizer = GLfloat(m_axisCacheY.max() - m_axisCacheY.min()) / 2.0f; #ifndef USE_UNIFORM_SCALING // Use this if we want to use autoscaling for x and z QSizeF areaSize; @@ -2014,8 +2046,9 @@ void Scatter3DRenderer::calculateSceneScalingFactors() m_axisCacheX.setScale(2.0f * m_graphAspectRatio); m_axisCacheZ.setScale(-m_axisCacheX.scale()); #endif - m_scaleXWithBackground = m_scaleX + m_backgroundMargin; - m_scaleZWithBackground = m_scaleZ + m_backgroundMargin; + m_scaleXWithBackground = m_scaleX + m_hBackgroundMargin; + m_scaleZWithBackground = m_scaleZ + m_hBackgroundMargin; + m_scaleYWithBackground = m_vBackgroundMargin + 1.0f; m_axisCacheX.setTranslate(-m_axisCacheX.scale() / 2.0f); m_axisCacheZ.setTranslate(-m_axisCacheZ.scale() / 2.0f); } diff --git a/src/datavisualization/engine/scatter3drenderer_p.h b/src/datavisualization/engine/scatter3drenderer_p.h index b5d188e1..645a2a8e 100644 --- a/src/datavisualization/engine/scatter3drenderer_p.h +++ b/src/datavisualization/engine/scatter3drenderer_p.h @@ -67,10 +67,11 @@ private: GLuint m_selectionDepthBuffer; GLfloat m_shadowQualityToShader; GLint m_shadowQualityMultiplier; - GLfloat m_heightNormalizer; + float m_heightNormalizer; float m_scaleX; float m_scaleZ; float m_scaleXWithBackground; + float m_scaleYWithBackground; float m_scaleZWithBackground; int m_selectedItemIndex; ScatterSeriesRenderCache *m_selectedSeriesCache; @@ -78,7 +79,6 @@ private: GLfloat m_dotSizeScale; bool m_hasHeightAdjustmentChanged; ScatterRenderItem m_dummyRenderItem; - GLfloat m_backgroundMargin; GLfloat m_maxItemSize; int m_clickedIndex; bool m_havePointSeries; @@ -95,6 +95,10 @@ public: SeriesRenderCache *createNewCache(QAbstract3DSeries *series); void updateItems(const QVector &items); void updateScene(Q3DScene *scene); + void updateAxisLabels(QAbstract3DAxis::AxisOrientation orientation, + const QStringList &labels); + void updateAxisTitleVisibility(QAbstract3DAxis::AxisOrientation orientation, + bool visible); QVector3D convertPositionToTranslation(const QVector3D &position, bool isAbsolute); diff --git a/src/datavisualization/engine/surface3drenderer.cpp b/src/datavisualization/engine/surface3drenderer.cpp index 91aeb77f..58c0e245 100644 --- a/src/datavisualization/engine/surface3drenderer.cpp +++ b/src/datavisualization/engine/surface3drenderer.cpp @@ -30,9 +30,6 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION //#define SHOW_DEPTH_TEXTURE_SCENE -// Margin for background (1.10 make it 10% larger to avoid -// selection ball being drawn inside background) -const GLfloat backgroundMargin = 1.1f; const GLfloat sliceZScale = 0.1f; const GLfloat sliceUnits = 2.5f; const uint greenMultiplier = 256; @@ -55,6 +52,7 @@ Surface3DRenderer::Surface3DRenderer(Surface3DController *controller) m_scaleX(0.0f), m_scaleZ(0.0f), m_scaleXWithBackground(0.0f), + m_scaleYWithBackground(0.0f), m_scaleZWithBackground(0.0f), m_depthModelTexture(0), m_depthFrameBuffer(0), @@ -889,7 +887,7 @@ void Surface3DRenderer::drawSlicedScene() } // Vertical lines - QVector3D gridLineScaleY(gridLineWidth, backgroundMargin, gridLineWidth); + QVector3D gridLineScaleY(gridLineWidth, m_scaleYWithBackground, gridLineWidth); gridLineCount = sliceCache.gridLineCount(); for (int line = 0; line < gridLineCount; line++) { @@ -957,7 +955,7 @@ void Surface3DRenderer::drawSlicedScene() labelNbr = 0; positionComp.setY(-0.1f); - labelTrans.setY(-backgroundMargin); + labelTrans.setY(-m_scaleYWithBackground); labelCount = sliceCache.labelCount(); for (int label = 0; label < labelCount; label++) { if (countLabelItems > labelNbr) { @@ -1053,7 +1051,7 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) / activeCamera->zoomLevel() / m_autoScaleAdjustment * 100.0f; qreal cameraAngle = qreal(activeCamera->yRotation()) / 180.0 * M_PI; float cameraYPos = float(qSin(cameraAngle)) * distanceToCenter; - m_yFlippedForGrid = cameraYPos < backgroundMargin; + m_yFlippedForGrid = cameraYPos < (m_scaleYWithBackground); } else if (m_useOrthoProjection && activeCamera->yRotation() == 0.0f) { // With ortho we only need to flip at angle zero, to fix label autorotation angles m_yFlippedForGrid = !m_yFlipped; @@ -1369,7 +1367,7 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) QMatrix4x4 MVPMatrix; QMatrix4x4 itModelMatrix; - QVector3D bgScale(m_scaleXWithBackground, backgroundMargin, m_scaleZWithBackground); + QVector3D bgScale(m_scaleXWithBackground, m_scaleYWithBackground, m_scaleZWithBackground); modelMatrix.scale(bgScale); // If we're viewing from below, background object must be flipped @@ -1433,7 +1431,7 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) // Draw grid lines QVector3D gridLineScaleX(m_scaleXWithBackground, gridLineWidth, gridLineWidth); QVector3D gridLineScaleZ(gridLineWidth, gridLineWidth, m_scaleZWithBackground); - QVector3D gridLineScaleY(gridLineWidth, backgroundMargin, gridLineWidth); + QVector3D gridLineScaleY(gridLineWidth, m_scaleYWithBackground, gridLineWidth); if (m_cachedTheme->isGridEnabled() && m_heightNormalizer) { #if !(defined QT_OPENGL_ES_2) @@ -1479,7 +1477,7 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) else lineXRotation = m_xRightAngleRotationNeg; - float yFloorLinePosition = -backgroundMargin + gridLineOffset; + float yFloorLinePosition = -m_scaleYWithBackground + gridLineOffset; if (m_yFlipped != m_flipHorizontalGrid) yFloorLinePosition = -yFloorLinePosition; @@ -1851,7 +1849,7 @@ void Surface3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa if (m_axisCacheZ.segmentCount() > 0) { int labelCount = m_axisCacheZ.labelCount(); float labelXTrans = m_scaleXWithBackground + labelMargin; - float labelYTrans = m_flipHorizontalGrid ? backgroundMargin : -backgroundMargin; + float labelYTrans = m_flipHorizontalGrid ? m_scaleYWithBackground : -m_scaleYWithBackground; if (m_polarGraph) { labelXTrans *= m_radialLabelOffset; // YTrans up only if over background @@ -1991,7 +1989,7 @@ void Surface3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa int labelCount = m_axisCacheX.labelCount(); float labelZTrans = 0.0f; - float labelYTrans = m_flipHorizontalGrid ? backgroundMargin : -backgroundMargin; + float labelYTrans = m_flipHorizontalGrid ? m_scaleYWithBackground : -m_scaleYWithBackground; if (m_polarGraph) labelYTrans += gridLineOffset + gridLineWidth; else @@ -2091,10 +2089,10 @@ void Surface3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa } float offsetValue = 0.0f; bool showLastLabel = false; - QVector &gridPositions = m_axisCacheX.formatter()->gridPositions(); - int lastGridPosIndex = gridPositions.size() - 1; - if (gridPositions.size() - && (gridPositions.at(lastGridPosIndex) != 1.0f || gridPositions.at(0) != 0.0f)) { + QVector &labelPositions = m_axisCacheX.formatter()->labelPositions(); + int lastLabelPosIndex = labelPositions.size() - 1; + if (labelPositions.size() + && (labelPositions.at(lastLabelPosIndex) != 1.0f || labelPositions.at(0) != 0.0f)) { // Avoid overlapping first and last label if they would get on same position showLastLabel = true; } @@ -2104,24 +2102,24 @@ void Surface3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa // Draw the label here if (m_polarGraph) { // Calculate angular position - if (label == lastGridPosIndex && !showLastLabel) + if (label == lastLabelPosIndex && !showLastLabel) continue; - float gridPosition = gridPositions.at(label); - qreal angle = gridPosition * M_PI * 2.0; - labelTrans.setX((m_graphAspectRatio + labelMargin)* float(qSin(angle))); + float labelPosition = labelPositions.at(label); + qreal angle = labelPosition * M_PI * 2.0; + labelTrans.setX((m_graphAspectRatio + labelMargin) * float(qSin(angle))); labelTrans.setZ(-(m_graphAspectRatio + labelMargin) * float(qCos(angle))); // Alignment depends on label angular position, as well as flips Qt::AlignmentFlag vAlignment = Qt::AlignCenter; Qt::AlignmentFlag hAlignment = Qt::AlignCenter; const float centerMargin = 0.005f; - if (gridPosition < 0.25f - centerMargin || gridPosition > 0.75f + centerMargin) + if (labelPosition < 0.25f - centerMargin || labelPosition > 0.75f + centerMargin) vAlignment = m_zFlipped ? Qt::AlignTop : Qt::AlignBottom; - else if (gridPosition > 0.25f + centerMargin && gridPosition < 0.75f - centerMargin) + else if (labelPosition > 0.25f + centerMargin && labelPosition < 0.75f - centerMargin) vAlignment = m_zFlipped ? Qt::AlignBottom : Qt::AlignTop; - if (gridPosition < 0.50f - centerMargin && gridPosition > centerMargin) + if (labelPosition < 0.50f - centerMargin && labelPosition > centerMargin) hAlignment = m_zFlipped ? Qt::AlignRight : Qt::AlignLeft; - else if (gridPosition < 1.0f - centerMargin && gridPosition > 0.5f + centerMargin) + else if (labelPosition < 1.0f - centerMargin && labelPosition > 0.5f + centerMargin) hAlignment = m_zFlipped ? Qt::AlignLeft : Qt::AlignRight; if (m_yFlippedForGrid && vAlignment != Qt::AlignCenter) vAlignment = (vAlignment == Qt::AlignTop) ? Qt::AlignBottom : Qt::AlignTop; @@ -2390,6 +2388,15 @@ void Surface3DRenderer::idToRGBA(uint id, uchar *r, uchar *g, uchar *b, uchar *a void Surface3DRenderer::calculateSceneScalingFactors() { + if (m_polarGraph) { + float polarMargin = calculatePolarBackgroundMargin(); + m_hBackgroundMargin = qMax(m_hBackgroundMargin, polarMargin); + } else { + // Margin for background (0.10 make it 10% larger to avoid + // selection ball being drawn inside background) + m_hBackgroundMargin = 0.1f; + } + // Calculate scene scaling and translation factors m_heightNormalizer = GLfloat(m_axisCacheY.max() - m_axisCacheY.min()); QSizeF areaSize; @@ -2403,8 +2410,9 @@ void Surface3DRenderer::calculateSceneScalingFactors() m_scaleX = m_graphAspectRatio * areaSize.width() / scaleFactor; m_scaleZ = m_graphAspectRatio * areaSize.height() / scaleFactor; } - m_scaleXWithBackground = m_scaleX + backgroundMargin - 1.0f; - m_scaleZWithBackground = m_scaleZ + backgroundMargin - 1.0f; + m_scaleXWithBackground = m_scaleX + m_hBackgroundMargin; + m_scaleZWithBackground = m_scaleZ + m_hBackgroundMargin; + m_scaleYWithBackground = m_vBackgroundMargin + 1.0f; float factorScaler = 2.0f * m_graphAspectRatio / scaleFactor; m_axisCacheX.setScale(factorScaler * areaSize.width()); @@ -2656,7 +2664,10 @@ void Surface3DRenderer::updateShadowQuality(QAbstract3DGraph::ShadowQuality qual void Surface3DRenderer::updateTextures() { - // Do nothing, but required as it is pure virtual on parent + Abstract3DRenderer::updateTextures(); + + if (m_polarGraph) + calculateSceneScalingFactors(); } void Surface3DRenderer::updateSlicingActive(bool isSlicing) @@ -2830,4 +2841,23 @@ QVector3D Surface3DRenderer::convertPositionToTranslation(const QVector3D &posit return QVector3D(xTrans, yTrans, zTrans); } +void Surface3DRenderer::updateAxisLabels(QAbstract3DAxis::AxisOrientation orientation, + const QStringList &labels) +{ + Abstract3DRenderer::updateAxisLabels(orientation, labels); + + // Angular axis label dimensions affect the chart dimensions + if (m_polarGraph && orientation == QAbstract3DAxis::AxisOrientationX) + calculateSceneScalingFactors(); +} + +void Surface3DRenderer::updateAxisTitleVisibility(QAbstract3DAxis::AxisOrientation orientation, bool visible) +{ + Abstract3DRenderer::updateAxisTitleVisibility(orientation, visible); + + // Angular axis title existence affects the chart dimensions + if (m_polarGraph && orientation == QAbstract3DAxis::AxisOrientationX) + calculateSceneScalingFactors(); +} + QT_END_NAMESPACE_DATAVISUALIZATION diff --git a/src/datavisualization/engine/surface3drenderer_p.h b/src/datavisualization/engine/surface3drenderer_p.h index db174a0d..7a547042 100644 --- a/src/datavisualization/engine/surface3drenderer_p.h +++ b/src/datavisualization/engine/surface3drenderer_p.h @@ -56,11 +56,12 @@ private: ShaderHelper *m_surfaceSliceSmoothShader; ShaderHelper *m_selectionShader; ShaderHelper *m_labelShader; - GLfloat m_heightNormalizer; - GLfloat m_scaleX; - GLfloat m_scaleZ; - GLfloat m_scaleXWithBackground; - GLfloat m_scaleZWithBackground; + float m_heightNormalizer; + float m_scaleX; + float m_scaleZ; + float m_scaleXWithBackground; + float m_scaleYWithBackground; + float m_scaleZWithBackground; GLuint m_depthModelTexture; GLuint m_depthFrameBuffer; GLuint m_selectionFrameBuffer; @@ -97,6 +98,10 @@ public: inline QPoint clickedPosition() const { return m_clickedPosition; } void resetClickedStatus(); QVector3D convertPositionToTranslation(const QVector3D &position, bool isAbsolute); + void updateAxisLabels(QAbstract3DAxis::AxisOrientation orientation, + const QStringList &labels); + void updateAxisTitleVisibility(QAbstract3DAxis::AxisOrientation orientation, + bool visible); void render(GLuint defaultFboHandle = 0); diff --git a/tests/scattertest/main.cpp b/tests/scattertest/main.cpp index f434e324..53e94083 100644 --- a/tests/scattertest/main.cpp +++ b/tests/scattertest/main.cpp @@ -240,7 +240,7 @@ int main(int argc, char **argv) optimizationStaticCB->setChecked(false); QCheckBox *orthoCB = new QCheckBox(widget); - orthoCB->setText(QStringLiteral("Orthogonal projection")); + orthoCB->setText(QStringLiteral("Orthographic projection")); orthoCB->setChecked(false); QCheckBox *polarCB = new QCheckBox(widget); -- cgit v1.2.3 From f8a79feee810ca036b4b85db9c41957008474d30 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Fri, 27 Jun 2014 13:53:32 +0300 Subject: Fix custom item position in polar graphs Task-number: QTRD-3184 Change-Id: Id2550a143021771341d5bfef7e08cd5868404ca5 Reviewed-by: Mika Salmela --- src/datavisualization/engine/scatter3drenderer.cpp | 12 ++++++++---- src/datavisualization/engine/surface3drenderer.cpp | 8 ++++++-- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/datavisualization/engine/scatter3drenderer.cpp b/src/datavisualization/engine/scatter3drenderer.cpp index 075f252f..852341a4 100644 --- a/src/datavisualization/engine/scatter3drenderer.cpp +++ b/src/datavisualization/engine/scatter3drenderer.cpp @@ -2227,13 +2227,17 @@ QVector3D Scatter3DRenderer::convertPositionToTranslation(const QVector3D &posit float yTrans = 0.0f; float zTrans = 0.0f; if (!isAbsolute) { - xTrans = m_axisCacheX.positionAt(position.x()); + if (m_polarGraph) { + calculatePolarXZ(position, xTrans, zTrans); + } else { + xTrans = m_axisCacheX.positionAt(position.x()); + zTrans = m_axisCacheZ.positionAt(position.z()); + } yTrans = m_axisCacheY.positionAt(position.y()); - zTrans = m_axisCacheZ.positionAt(position.z()); } else { - xTrans = position.x() * m_axisCacheX.scale() / 2.0f; + xTrans = position.x() * m_scaleX; yTrans = position.y(); - zTrans = position.z() * m_axisCacheZ.scale() / 2.0f; + zTrans = position.z() * m_scaleZ; } return QVector3D(xTrans, yTrans, zTrans); } diff --git a/src/datavisualization/engine/surface3drenderer.cpp b/src/datavisualization/engine/surface3drenderer.cpp index 58c0e245..50104a93 100644 --- a/src/datavisualization/engine/surface3drenderer.cpp +++ b/src/datavisualization/engine/surface3drenderer.cpp @@ -2830,9 +2830,13 @@ QVector3D Surface3DRenderer::convertPositionToTranslation(const QVector3D &posit float yTrans = 0.0f; float zTrans = 0.0f; if (!isAbsolute) { - xTrans = m_axisCacheX.positionAt(position.x()); + if (m_polarGraph) { + calculatePolarXZ(position, xTrans, zTrans); + } else { + xTrans = m_axisCacheX.positionAt(position.x()); + zTrans = m_axisCacheZ.positionAt(position.z()); + } yTrans = m_axisCacheY.positionAt(position.y()); - zTrans = m_axisCacheZ.positionAt(position.z()); } else { xTrans = position.x() * m_scaleX; yTrans = position.y(); -- cgit v1.2.3 From a1293a58e0909d56e34654549ba719bacfdda1bb Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Mon, 30 Jun 2014 10:33:33 +0300 Subject: Added horizontal aspect ratio property for abstract graphs Value 0.0 indicates automatic scaling (the default). Task-number: QTRD-3192 Change-Id: I5e4cc1b7a03c0ba811e2ed3916a440906429af57 Reviewed-by: Mika Salmela --- .../qmlspectrogram/qml/qmlspectrogram/main.qml | 1 + ...tdatavisualization-qml-abstractdeclarative.qdoc | 21 +++++++++-- .../engine/abstract3dcontroller.cpp | 30 +++++++++++++--- .../engine/abstract3dcontroller_p.h | 15 +++++--- .../engine/abstract3drenderer.cpp | 9 +++++ .../engine/abstract3drenderer_p.h | 2 ++ src/datavisualization/engine/qabstract3dgraph.cpp | 34 ++++++++++++++++-- src/datavisualization/engine/qabstract3dgraph.h | 5 +++ src/datavisualization/engine/scatter3drenderer.cpp | 41 +++++++++++----------- src/datavisualization/engine/surface3drenderer.cpp | 25 ++++++++----- src/datavisualizationqml2/abstractdeclarative.cpp | 14 +++++++- src/datavisualizationqml2/abstractdeclarative_p.h | 5 +++ tests/scattertest/main.cpp | 15 ++++++-- tests/scattertest/scatterchart.cpp | 8 ++++- tests/scattertest/scatterchart.h | 1 + tests/surfacetest/graphmodifier.cpp | 8 ++++- tests/surfacetest/graphmodifier.h | 1 + tests/surfacetest/main.cpp | 11 +++++- 18 files changed, 199 insertions(+), 47 deletions(-) diff --git a/examples/datavisualization/qmlspectrogram/qml/qmlspectrogram/main.qml b/examples/datavisualization/qmlspectrogram/qml/qmlspectrogram/main.qml index f7e93827..af16539b 100644 --- a/examples/datavisualization/qmlspectrogram/qml/qmlspectrogram/main.qml +++ b/examples/datavisualization/qmlspectrogram/qml/qmlspectrogram/main.qml @@ -109,6 +109,7 @@ Window { orthoProjection: true flipHorizontalGrid: true radialLabelOffset: 0.01 // Add little offset so the labels do not overlap + horizontalAspectRatio: 1 Surface3DSeries { id: surfaceSeries diff --git a/src/datavisualization/doc/src/qtdatavisualization-qml-abstractdeclarative.qdoc b/src/datavisualization/doc/src/qtdatavisualization-qml-abstractdeclarative.qdoc index cae9a406..c83beb74 100644 --- a/src/datavisualization/doc/src/qtdatavisualization-qml-abstractdeclarative.qdoc +++ b/src/datavisualization/doc/src/qtdatavisualization-qml-abstractdeclarative.qdoc @@ -315,10 +315,27 @@ * \qmlproperty real AbstractGraph3D::aspectRatio * \since QtDataVisualization 1.1 * - * Aspect ratio of the graph data. This is the ratio of data scaling between horizontal and - * vertical axes. Defaults to \c{2.0}. + * The aspect ratio is the ratio of the graph scaling between the longest axis on the horizontal + * plane and the Y-axis. Defaults to \c{2.0}. * * \note Has no effect on Bars3D. + * + * \sa horizontalAspectRatio + */ + +/*! + * \qmlproperty real AbstractGraph3D::horizontalAspectRatio + * \since QtDataVisualization 1.2 + * + * The horizontal aspect ratio is the ratio of the graph scaling between the X and Z axes. + * Value of 0.0 indicates automatic scaling according to axis ranges. + * Defaults to \c{0.0}. + * + * \note Has no effect on Bars3D, which handles scaling on the horizontal plane via + * \l{Bars3D::barThickness}{barThickness} and \l{Bars3D::barSpacing}{barSpacing} properties. + * Polar graphs also ignore this property. + * + * \sa aspectRatio, polar, Bars3D::barThickness, Bars3D::barSpacing */ /*! diff --git a/src/datavisualization/engine/abstract3dcontroller.cpp b/src/datavisualization/engine/abstract3dcontroller.cpp index 6b090fcd..1b497490 100644 --- a/src/datavisualization/engine/abstract3dcontroller.cpp +++ b/src/datavisualization/engine/abstract3dcontroller.cpp @@ -37,7 +37,8 @@ Abstract3DController::Abstract3DController(QRect initialViewport, Q3DScene *scen m_selectionMode(QAbstract3DGraph::SelectionItem), m_shadowQuality(QAbstract3DGraph::ShadowQualityMedium), m_useOrthoProjection(false), - m_aspectRatio(2.0f), + m_aspectRatio(2.0), + m_horizontalAspectRatio(0.0), m_optimizationHints(QAbstract3DGraph::OptimizationDefault), m_scene(scene), m_activeInputHandler(0), @@ -204,10 +205,15 @@ void Abstract3DController::synchDataToRenderer() } if (m_changeTracker.aspectRatioChanged) { - m_renderer->updateAspectRatio(m_aspectRatio); + m_renderer->updateAspectRatio(float(m_aspectRatio)); m_changeTracker.aspectRatioChanged = false; } + if (m_changeTracker.horizontalAspectRatioChanged) { + m_renderer->updateHorizontalAspectRatio(float(m_horizontalAspectRatio)); + m_changeTracker.horizontalAspectRatioChanged = false; + } + if (m_changeTracker.optimizationHintChanged) { m_renderer->updateOptimizationHint(m_optimizationHints); m_changeTracker.optimizationHintChanged = false; @@ -1517,7 +1523,7 @@ bool Abstract3DController::isOrthoProjection() const return m_useOrthoProjection; } -void Abstract3DController::setAspectRatio(float ratio) +void Abstract3DController::setAspectRatio(qreal ratio) { if (m_aspectRatio != ratio) { m_aspectRatio = ratio; @@ -1528,11 +1534,27 @@ void Abstract3DController::setAspectRatio(float ratio) } } -float Abstract3DController::aspectRatio() +qreal Abstract3DController::aspectRatio() { return m_aspectRatio; } +void Abstract3DController::setHorizontalAspectRatio(qreal ratio) +{ + if (m_horizontalAspectRatio != ratio) { + m_horizontalAspectRatio = ratio; + m_changeTracker.horizontalAspectRatioChanged = true; + emit horizontalAspectRatioChanged(m_horizontalAspectRatio); + m_isDataDirty = true; + emitNeedRender(); + } +} + +qreal Abstract3DController::horizontalAspectRatio() const +{ + return m_horizontalAspectRatio; +} + void Abstract3DController::setPolar(bool enable) { if (enable != m_isPolar) { diff --git a/src/datavisualization/engine/abstract3dcontroller_p.h b/src/datavisualization/engine/abstract3dcontroller_p.h index 6394da1e..f6cfb057 100644 --- a/src/datavisualization/engine/abstract3dcontroller_p.h +++ b/src/datavisualization/engine/abstract3dcontroller_p.h @@ -84,6 +84,7 @@ struct Abstract3DChangeBitField { bool axisYLabelAutoRotationChanged : 1; bool axisZLabelAutoRotationChanged : 1; bool aspectRatioChanged : 1; + bool horizontalAspectRatioChanged : 1; bool axisXTitleVisibilityChanged : 1; bool axisYTitleVisibilityChanged : 1; bool axisZTitleVisibilityChanged : 1; @@ -130,6 +131,7 @@ struct Abstract3DChangeBitField { axisYLabelAutoRotationChanged(true), axisZLabelAutoRotationChanged(true), aspectRatioChanged(true), + horizontalAspectRatioChanged(true), axisXTitleVisibilityChanged(true), axisYTitleVisibilityChanged(true), axisZTitleVisibilityChanged(true), @@ -160,7 +162,8 @@ private: QAbstract3DGraph::SelectionFlags m_selectionMode; QAbstract3DGraph::ShadowQuality m_shadowQuality; bool m_useOrthoProjection; - float m_aspectRatio; + qreal m_aspectRatio; + qreal m_horizontalAspectRatio; QAbstract3DGraph::OptimizationHints m_optimizationHints; protected: @@ -273,8 +276,11 @@ public: QAbstract3DGraph::ElementType selectedElement() const; - void setAspectRatio(float ratio); - float aspectRatio(); + void setAspectRatio(qreal ratio); + qreal aspectRatio(); + void setHorizontalAspectRatio(qreal ratio); + qreal horizontalAspectRatio() const; + void setPolar(bool enable); bool isPolar() const; void setRadialLabelOffset(float offset); @@ -354,7 +360,8 @@ signals: void measureFpsChanged(bool enabled); void currentFpsChanged(qreal fps); void orthoProjectionChanged(bool enabled); - void aspectRatioChanged(float ratio); + void aspectRatioChanged(qreal ratio); + void horizontalAspectRatioChanged(qreal ratio); void optimizationHintsChanged(QAbstract3DGraph::OptimizationHints hints); void polarChanged(bool enabled); void radialLabelOffsetChanged(float offset); diff --git a/src/datavisualization/engine/abstract3drenderer.cpp b/src/datavisualization/engine/abstract3drenderer.cpp index 19fdf53b..782b1480 100644 --- a/src/datavisualization/engine/abstract3drenderer.cpp +++ b/src/datavisualization/engine/abstract3drenderer.cpp @@ -66,6 +66,7 @@ Abstract3DRenderer::Abstract3DRenderer(Abstract3DController *controller) m_gridLineObj(0), m_labelObj(0), m_graphAspectRatio(2.0f), + m_graphHorizontalAspectRatio(0.0f), m_polarGraph(false), m_radialLabelOffset(1.0f), m_xRightAngleRotation(QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, 90.0f)), @@ -319,6 +320,14 @@ void Abstract3DRenderer::updateAspectRatio(float ratio) updateCustomItemPositions(); } +void Abstract3DRenderer::updateHorizontalAspectRatio(float ratio) +{ + m_graphHorizontalAspectRatio = ratio; + foreach (SeriesRenderCache *cache, m_renderCacheList) + cache->setDataDirty(true); + updateCustomItemPositions(); +} + void Abstract3DRenderer::updatePolar(bool enable) { m_polarGraph = enable; diff --git a/src/datavisualization/engine/abstract3drenderer_p.h b/src/datavisualization/engine/abstract3drenderer_p.h index 0c24c41e..f7fa748b 100644 --- a/src/datavisualization/engine/abstract3drenderer_p.h +++ b/src/datavisualization/engine/abstract3drenderer_p.h @@ -125,6 +125,7 @@ public: virtual void updateCustomItem(CustomRenderItem *renderItem); virtual void updateAspectRatio(float ratio); + virtual void updateHorizontalAspectRatio(float ratio); virtual void updatePolar(bool enable); virtual void updateRadialLabelOffset(float offset); @@ -256,6 +257,7 @@ protected: ObjectHelper *m_labelObj; // Shared reference float m_graphAspectRatio; + float m_graphHorizontalAspectRatio; bool m_polarGraph; float m_radialLabelOffset; diff --git a/src/datavisualization/engine/qabstract3dgraph.cpp b/src/datavisualization/engine/qabstract3dgraph.cpp index af809100..23290d69 100644 --- a/src/datavisualization/engine/qabstract3dgraph.cpp +++ b/src/datavisualization/engine/qabstract3dgraph.cpp @@ -615,14 +615,16 @@ bool QAbstract3DGraph::isOrthoProjection() const * \property QAbstract3DGraph::aspectRatio * \since QtDataVisualization 1.1 * - * Aspect ratio of the graph data. This is the ratio of data scaling between horizontal and - * vertical axes. Defaults to \c{2.0}. + * The aspect ratio is the ratio of the graph scaling between the longest axis on the horizontal + * plane and the Y-axis. Defaults to \c{2.0}. * * \note Has no effect on Q3DBars. + * + * \sa horizontalAspectRatio */ void QAbstract3DGraph::setAspectRatio(qreal ratio) { - d_ptr->m_visualController->setAspectRatio(float(ratio)); + d_ptr->m_visualController->setAspectRatio(ratio); } qreal QAbstract3DGraph::aspectRatio() const @@ -696,6 +698,30 @@ float QAbstract3DGraph::radialLabelOffset() const return d_ptr->m_visualController->radialLabelOffset(); } +/*! + * \property QAbstract3DGraph::horizontalAspectRatio + * \since QtDataVisualization 1.2 + * + * The horizontal aspect ratio is the ratio of the graph scaling between the X and Z axes. + * Value of 0.0 indicates automatic scaling according to axis ranges. + * Defaults to \c{0.0}. + * + * \note Has no effect on Q3DBars, which handles scaling on the horizontal plane via + * \l{Q3DBars::barThickness}{barThickness} and \l{Q3DBars::barSpacing}{barSpacing} properties. + * Polar graphs also ignore this property. + * + * \sa aspectRatio, polar, Q3DBars::barThickness, Q3DBars::barSpacing + */ +void QAbstract3DGraph::setHorizontalAspectRatio(qreal ratio) +{ + d_ptr->m_visualController->setHorizontalAspectRatio(ratio); +} + +qreal QAbstract3DGraph::horizontalAspectRatio() const +{ + return d_ptr->m_visualController->horizontalAspectRatio(); +} + /*! * \internal */ @@ -850,6 +876,8 @@ void QAbstract3DGraphPrivate::setVisualController(Abstract3DController *controll &QAbstract3DGraph::polarChanged); QObject::connect(m_visualController, &Abstract3DController::radialLabelOffsetChanged, q_ptr, &QAbstract3DGraph::radialLabelOffsetChanged); + QObject::connect(m_visualController, &Abstract3DController::horizontalAspectRatioChanged, q_ptr, + &QAbstract3DGraph::horizontalAspectRatioChanged); } void QAbstract3DGraphPrivate::handleDevicePixelRatioChange() diff --git a/src/datavisualization/engine/qabstract3dgraph.h b/src/datavisualization/engine/qabstract3dgraph.h index e6ebc42b..ba099395 100644 --- a/src/datavisualization/engine/qabstract3dgraph.h +++ b/src/datavisualization/engine/qabstract3dgraph.h @@ -52,6 +52,7 @@ class QT_DATAVISUALIZATION_EXPORT QAbstract3DGraph : public QWindow, protected Q Q_PROPERTY(OptimizationHints optimizationHints READ optimizationHints WRITE setOptimizationHints NOTIFY optimizationHintsChanged) Q_PROPERTY(bool polar READ isPolar WRITE setPolar NOTIFY polarChanged) Q_PROPERTY(float radialLabelOffset READ radialLabelOffset WRITE setRadialLabelOffset NOTIFY radialLabelOffsetChanged) + Q_PROPERTY(qreal horizontalAspectRatio READ horizontalAspectRatio WRITE setHorizontalAspectRatio NOTIFY horizontalAspectRatioChanged) protected: explicit QAbstract3DGraph(QAbstract3DGraphPrivate *d, const QSurfaceFormat *format, @@ -158,6 +159,9 @@ public: void setRadialLabelOffset(float offset); float radialLabelOffset() const; + void setHorizontalAspectRatio(qreal ratio); + qreal horizontalAspectRatio() const; + protected: bool event(QEvent *event); void resizeEvent(QResizeEvent *event); @@ -183,6 +187,7 @@ signals: void optimizationHintsChanged(QAbstract3DGraph::OptimizationHints hints); void polarChanged(bool enabled); void radialLabelOffsetChanged(float offset); + void horizontalAspectRatioChanged(qreal ratio); private: Q_DISABLE_COPY(QAbstract3DGraph) diff --git a/src/datavisualization/engine/scatter3drenderer.cpp b/src/datavisualization/engine/scatter3drenderer.cpp index 852341a4..56219339 100644 --- a/src/datavisualization/engine/scatter3drenderer.cpp +++ b/src/datavisualization/engine/scatter3drenderer.cpp @@ -33,8 +33,6 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION -//#define USE_UNIFORM_SCALING // Scale x and z uniformly, or based on autoscaled values - const GLfloat defaultMinSize = 0.01f; const GLfloat defaultMaxSize = 0.1f; const GLfloat itemScaler = 3.0f; @@ -2025,30 +2023,33 @@ void Scatter3DRenderer::calculateSceneScalingFactors() } m_heightNormalizer = GLfloat(m_axisCacheY.max() - m_axisCacheY.min()) / 2.0f; -#ifndef USE_UNIFORM_SCALING // Use this if we want to use autoscaling for x and z + + float horizontalAspectRatio; + if (m_polarGraph) + horizontalAspectRatio = 1.0f; + else + horizontalAspectRatio = m_graphHorizontalAspectRatio; + QSizeF areaSize; - areaSize.setHeight((m_axisCacheZ.max() - m_axisCacheZ.min())); - areaSize.setWidth((m_axisCacheX.max() - m_axisCacheX.min())); - float scaleFactor = qMax(areaSize.width(), areaSize.height()); - if (m_polarGraph) { - m_scaleX = m_graphAspectRatio; - m_scaleZ = m_graphAspectRatio; + if (horizontalAspectRatio == 0.0f) { + areaSize.setHeight(m_axisCacheZ.max() - m_axisCacheZ.min()); + areaSize.setWidth(m_axisCacheX.max() - m_axisCacheX.min()); } else { - m_scaleX = m_graphAspectRatio * areaSize.width() / scaleFactor; - m_scaleZ = m_graphAspectRatio * areaSize.height() / scaleFactor; + areaSize.setHeight(1.0f); + areaSize.setWidth(horizontalAspectRatio); } - float factorScaler = 2.0f * m_graphAspectRatio / scaleFactor; - m_axisCacheX.setScale(factorScaler * areaSize.width()); - m_axisCacheZ.setScale(-factorScaler * areaSize.height()); -#else // ..and this if we want uniform scaling based on largest dimension - m_scaleX = m_graphAspectRatio; - m_scaleZ = m_graphAspectRatio; - m_axisCacheX.setScale(2.0f * m_graphAspectRatio); - m_axisCacheZ.setScale(-m_axisCacheX.scale()); -#endif + + float scaleFactor = qMax(areaSize.width(), areaSize.height()); + m_scaleX = m_graphAspectRatio * areaSize.width() / scaleFactor; + m_scaleZ = m_graphAspectRatio * areaSize.height() / scaleFactor; + m_scaleXWithBackground = m_scaleX + m_hBackgroundMargin; m_scaleZWithBackground = m_scaleZ + m_hBackgroundMargin; m_scaleYWithBackground = m_vBackgroundMargin + 1.0f; + + float factorScaler = 2.0f * m_graphAspectRatio / scaleFactor; + m_axisCacheX.setScale(factorScaler * areaSize.width()); + m_axisCacheZ.setScale(-factorScaler * areaSize.height()); m_axisCacheX.setTranslate(-m_axisCacheX.scale() / 2.0f); m_axisCacheZ.setTranslate(-m_axisCacheZ.scale() / 2.0f); } diff --git a/src/datavisualization/engine/surface3drenderer.cpp b/src/datavisualization/engine/surface3drenderer.cpp index 50104a93..c1a23493 100644 --- a/src/datavisualization/engine/surface3drenderer.cpp +++ b/src/datavisualization/engine/surface3drenderer.cpp @@ -2399,17 +2399,26 @@ void Surface3DRenderer::calculateSceneScalingFactors() // Calculate scene scaling and translation factors m_heightNormalizer = GLfloat(m_axisCacheY.max() - m_axisCacheY.min()); + + float horizontalAspectRatio; + if (m_polarGraph) + horizontalAspectRatio = 1.0f; + else + horizontalAspectRatio = m_graphHorizontalAspectRatio; + QSizeF areaSize; - areaSize.setHeight(m_axisCacheZ.max() - m_axisCacheZ.min()); - areaSize.setWidth(m_axisCacheX.max() - m_axisCacheX.min()); - float scaleFactor = qMax(areaSize.width(), areaSize.height()); - if (m_polarGraph) { - m_scaleX = m_graphAspectRatio; - m_scaleZ = m_graphAspectRatio; + if (horizontalAspectRatio == 0.0f) { + areaSize.setHeight(m_axisCacheZ.max() - m_axisCacheZ.min()); + areaSize.setWidth(m_axisCacheX.max() - m_axisCacheX.min()); } else { - m_scaleX = m_graphAspectRatio * areaSize.width() / scaleFactor; - m_scaleZ = m_graphAspectRatio * areaSize.height() / scaleFactor; + areaSize.setHeight(1.0f); + areaSize.setWidth(horizontalAspectRatio); } + + float scaleFactor = qMax(areaSize.width(), areaSize.height()); + m_scaleX = m_graphAspectRatio * areaSize.width() / scaleFactor; + m_scaleZ = m_graphAspectRatio * areaSize.height() / scaleFactor; + m_scaleXWithBackground = m_scaleX + m_hBackgroundMargin; m_scaleZWithBackground = m_scaleZ + m_hBackgroundMargin; m_scaleYWithBackground = m_vBackgroundMargin + 1.0f; diff --git a/src/datavisualizationqml2/abstractdeclarative.cpp b/src/datavisualizationqml2/abstractdeclarative.cpp index 5182e96a..8983046b 100644 --- a/src/datavisualizationqml2/abstractdeclarative.cpp +++ b/src/datavisualizationqml2/abstractdeclarative.cpp @@ -333,6 +333,8 @@ void AbstractDeclarative::setSharedController(Abstract3DController *controller) &AbstractDeclarative::polarChanged); QObject::connect(m_controller.data(), &Abstract3DController::radialLabelOffsetChanged, this, &AbstractDeclarative::radialLabelOffsetChanged); + QObject::connect(m_controller.data(), &Abstract3DController::horizontalAspectRatioChanged, this, + &AbstractDeclarative::horizontalAspectRatioChanged); } void AbstractDeclarative::activateOpenGLContext(QQuickWindow *window) @@ -708,7 +710,7 @@ AbstractDeclarative::ElementType AbstractDeclarative::selectedElement() const void AbstractDeclarative::setAspectRatio(qreal ratio) { - m_controller->setAspectRatio(float(ratio)); + m_controller->setAspectRatio(ratio); } qreal AbstractDeclarative::aspectRatio() const @@ -748,6 +750,16 @@ float AbstractDeclarative::radialLabelOffset() const return m_controller->radialLabelOffset(); } +void AbstractDeclarative::setHorizontalAspectRatio(qreal ratio) +{ + m_controller->setHorizontalAspectRatio(ratio); +} + +qreal AbstractDeclarative::horizontalAspectRatio() const +{ + return m_controller->horizontalAspectRatio(); +} + void AbstractDeclarative::windowDestroyed(QObject *obj) { // Remove destroyed window from window lists diff --git a/src/datavisualizationqml2/abstractdeclarative_p.h b/src/datavisualizationqml2/abstractdeclarative_p.h index 379e346f..058cb44a 100644 --- a/src/datavisualizationqml2/abstractdeclarative_p.h +++ b/src/datavisualizationqml2/abstractdeclarative_p.h @@ -74,6 +74,7 @@ class AbstractDeclarative : public QQuickItem Q_PROPERTY(OptimizationHints optimizationHints READ optimizationHints WRITE setOptimizationHints NOTIFY optimizationHintsChanged REVISION 1) Q_PROPERTY(bool polar READ isPolar WRITE setPolar NOTIFY polarChanged REVISION 2) Q_PROPERTY(float radialLabelOffset READ radialLabelOffset WRITE setRadialLabelOffset NOTIFY radialLabelOffsetChanged REVISION 2) + Q_PROPERTY(qreal horizontalAspectRatio READ horizontalAspectRatio WRITE setHorizontalAspectRatio NOTIFY horizontalAspectRatioChanged REVISION 2) public: enum SelectionFlag { @@ -201,6 +202,9 @@ public: void setRadialLabelOffset(float offset); float radialLabelOffset() const; + void setHorizontalAspectRatio(qreal ratio); + qreal horizontalAspectRatio() const; + public slots: virtual void handleAxisXChanged(QAbstract3DAxis *axis) = 0; virtual void handleAxisYChanged(QAbstract3DAxis *axis) = 0; @@ -240,6 +244,7 @@ signals: Q_REVISION(1) void optimizationHintsChanged(AbstractDeclarative::OptimizationHints hints); Q_REVISION(2) void polarChanged(bool enabled); Q_REVISION(2) void radialLabelOffsetChanged(float offset); + Q_REVISION(2) void horizontalAspectRatioChanged(qreal ratio); private: QPointer m_controller; diff --git a/tests/scattertest/main.cpp b/tests/scattertest/main.cpp index 53e94083..55515cb0 100644 --- a/tests/scattertest/main.cpp +++ b/tests/scattertest/main.cpp @@ -235,6 +235,13 @@ int main(int argc, char **argv) aspectRatioSlider->setValue(20); aspectRatioSlider->setMaximum(100); + QSlider *horizontalAspectRatioSlider = new QSlider(Qt::Horizontal, widget); + horizontalAspectRatioSlider->setTickInterval(30); + horizontalAspectRatioSlider->setTickPosition(QSlider::TicksBelow); + horizontalAspectRatioSlider->setMinimum(0); + horizontalAspectRatioSlider->setValue(0); + horizontalAspectRatioSlider->setMaximum(300); + QCheckBox *optimizationStaticCB = new QCheckBox(widget); optimizationStaticCB->setText(QStringLiteral("Static optimization")); optimizationStaticCB->setChecked(false); @@ -315,8 +322,10 @@ int main(int argc, char **argv) vLayout2->addWidget(fontList); vLayout2->addWidget(new QLabel(QStringLiteral("Adjust font size"))); vLayout2->addWidget(fontSizeSlider); - vLayout2->addWidget(new QLabel(QStringLiteral("Adjust aspect ratio"))); - vLayout2->addWidget(aspectRatioSlider, 1, Qt::AlignTop); + vLayout2->addWidget(new QLabel(QStringLiteral("Adjust vertical aspect ratio"))); + vLayout2->addWidget(aspectRatioSlider); + vLayout2->addWidget(new QLabel(QStringLiteral("Adjust horizontal aspect ratio"))); + vLayout2->addWidget(horizontalAspectRatioSlider, 1, Qt::AlignTop); vLayout2->addWidget(optimizationStaticCB); vLayout2->addWidget(orthoCB); vLayout2->addWidget(polarCB); @@ -427,6 +436,8 @@ int main(int argc, char **argv) &ScatterDataModifier::changeLabelRotation); QObject::connect(aspectRatioSlider, &QSlider::valueChanged, modifier, &ScatterDataModifier::setAspectRatio); + QObject::connect(horizontalAspectRatioSlider, &QSlider::valueChanged, modifier, + &ScatterDataModifier::setHorizontalAspectRatio); QObject::connect(radialLabelSlider, &QSlider::valueChanged, modifier, &ScatterDataModifier::changeRadialLabelOffset); diff --git a/tests/scattertest/scatterchart.cpp b/tests/scattertest/scatterchart.cpp index 5a0a5976..5db772b5 100644 --- a/tests/scattertest/scatterchart.cpp +++ b/tests/scattertest/scatterchart.cpp @@ -1042,10 +1042,16 @@ void ScatterDataModifier::setMaxZ(int max) void ScatterDataModifier::setAspectRatio(int ratio) { - float aspectRatio = float(ratio) / 10.0f; + qreal aspectRatio = qreal(ratio) / 10.0; m_chart->setAspectRatio(aspectRatio); } +void ScatterDataModifier::setHorizontalAspectRatio(int ratio) +{ + qreal aspectRatio = qreal(ratio) / 100.0; + m_chart->setHorizontalAspectRatio(aspectRatio); +} + QVector3D ScatterDataModifier::randVector() { QVector3D retvec = QVector3D( diff --git a/tests/scattertest/scatterchart.h b/tests/scattertest/scatterchart.h index e2e979f3..df2494b6 100644 --- a/tests/scattertest/scatterchart.h +++ b/tests/scattertest/scatterchart.h @@ -53,6 +53,7 @@ public: void setMaxY(int max); void setMaxZ(int max); void setAspectRatio(int ratio); + void setHorizontalAspectRatio(int ratio); void start(); void massiveDataTest(); void massiveTestScroll(); diff --git a/tests/surfacetest/graphmodifier.cpp b/tests/surfacetest/graphmodifier.cpp index 56cd23cc..1f5a2f8f 100644 --- a/tests/surfacetest/graphmodifier.cpp +++ b/tests/surfacetest/graphmodifier.cpp @@ -1574,6 +1574,12 @@ void GraphModifier::updateSamples() void GraphModifier::setAspectRatio(int ratio) { - float aspectRatio = float(ratio) / 10.0f; + qreal aspectRatio = qreal(ratio) / 10.0; m_graph->setAspectRatio(aspectRatio); } + +void GraphModifier::setHorizontalAspectRatio(int ratio) +{ + qreal aspectRatio = qreal(ratio) / 100.0; + m_graph->setHorizontalAspectRatio(aspectRatio); +} diff --git a/tests/surfacetest/graphmodifier.h b/tests/surfacetest/graphmodifier.h index e70d35f2..f472c1ec 100644 --- a/tests/surfacetest/graphmodifier.h +++ b/tests/surfacetest/graphmodifier.h @@ -114,6 +114,7 @@ public: void testDataOrdering(); void setAspectRatio(int ratio); + void setHorizontalAspectRatio(int ratio); public slots: void changeShadowQuality(int quality); diff --git a/tests/surfacetest/main.cpp b/tests/surfacetest/main.cpp index 87f009cb..95d4637e 100644 --- a/tests/surfacetest/main.cpp +++ b/tests/surfacetest/main.cpp @@ -223,6 +223,11 @@ int main(int argc, char *argv[]) aspectRatioSlider->setValue(20); aspectRatioSlider->setMaximum(100); + QSlider *horizontalAspectRatioSlider = new QSlider(Qt::Horizontal, widget); + horizontalAspectRatioSlider->setMinimum(0); + horizontalAspectRatioSlider->setValue(0); + horizontalAspectRatioSlider->setMaximum(300); + QLinearGradient gr(0, 0, 100, 1); gr.setColorAt(0.0, Qt::black); gr.setColorAt(0.33, Qt::blue); @@ -435,8 +440,10 @@ int main(int argc, char *argv[]) vLayout->addWidget(gridSliderX); vLayout->addWidget(gridSliderZ); #endif - vLayout->addWidget(new QLabel(QStringLiteral("Adjust aspect ratio"))); + vLayout->addWidget(new QLabel(QStringLiteral("Adjust vertical aspect ratio"))); vLayout->addWidget(aspectRatioSlider); + vLayout->addWidget(new QLabel(QStringLiteral("Adjust horizontal aspect ratio"))); + vLayout->addWidget(horizontalAspectRatioSlider); vLayout->addWidget(new QLabel(QStringLiteral("Adjust axis range"))); vLayout->addWidget(axisRangeSliderX); vLayout->addWidget(axisRangeSliderY); @@ -660,6 +667,8 @@ int main(int argc, char *argv[]) QObject::connect(aspectRatioSlider, &QSlider::valueChanged, modifier, &GraphModifier::setAspectRatio); + QObject::connect(horizontalAspectRatioSlider, &QSlider::valueChanged, + modifier, &GraphModifier::setHorizontalAspectRatio); #ifdef MULTI_SERIES modifier->setSeries1CB(series1CB); -- cgit v1.2.3 From 4e9ed2481a1960a2fc4a31ecd14d1904b76cad2e Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Tue, 1 Jul 2014 15:47:07 +0300 Subject: Fix labels and grid lines changing size when aspect ratio is changed Since shaders needed fixing anyway for surface because of this, also implements the support for object gradients for surface, which was missing. Task-number: QTRD-2666 Task-number: QTRD-3211 Change-Id: I0c5da7fdfef308a96ec0bae4750fd22035da4e82 Reviewed-by: Mika Salmela --- README | 1 - .../doc/src/qtdatavisualization.qdoc | 1 - .../engine/abstract3drenderer.cpp | 25 +++-- .../engine/abstract3drenderer_p.h | 1 + src/datavisualization/engine/scatter3drenderer.cpp | 68 +++++++----- src/datavisualization/engine/scatter3drenderer_p.h | 2 +- src/datavisualization/engine/shaders/surface.frag | 4 +- .../engine/shaders/surfaceFlat.frag | 4 +- .../engine/shaders/surfaceShadowFlat.frag | 6 +- .../engine/shaders/surfaceShadowNoTex.frag | 6 +- .../engine/shaders/surface_ES2.frag | 4 +- src/datavisualization/engine/surface3drenderer.cpp | 68 ++++++++---- src/datavisualization/engine/surface3drenderer_p.h | 1 + src/datavisualization/utils/surfaceobject.cpp | 115 +++++++-------------- src/datavisualization/utils/surfaceobject_p.h | 6 ++ tests/surfacetest/graphmodifier.cpp | 19 +++- 16 files changed, 178 insertions(+), 153 deletions(-) diff --git a/README b/README index da0715d0..911edbb2 100644 --- a/README +++ b/README @@ -81,7 +81,6 @@ Known Issues - Surfaces with non-straight rows and columns do not always render properly. - Q3DLight class (and Light3D QML item) are currently not usable for anything. - Changing any of Q3DScene properties affecting subviewports currently has no effect. -- The color style Q3DTheme::ColorStyleObjectGradient doesn't work for surface graphs. - Widget based examples layout incorrectly in iOS. - Reparenting a graph to an item in another QQuickWindow is not supported. - There is a low-impact binary break between 1.0 and 1.1. The break is due to a QML type diff --git a/src/datavisualization/doc/src/qtdatavisualization.qdoc b/src/datavisualization/doc/src/qtdatavisualization.qdoc index af419814..3726e735 100644 --- a/src/datavisualization/doc/src/qtdatavisualization.qdoc +++ b/src/datavisualization/doc/src/qtdatavisualization.qdoc @@ -330,7 +330,6 @@ \li Surfaces with non-straight rows and columns do not always render properly. \li Q3DLight class (and Light3D QML item) are currently not usable for anything. \li Changing any of Q3DScene properties affecting subviewports currently has no effect. - \li The color style Q3DTheme::ColorStyleObjectGradient doesn't work for surface graphs. \li Widget based examples layout incorrectly in iOS. \li Reparenting a graph to an item in another QQuickWindow is not supported. \li There is a low-impact binary break between 1.0 and 1.1. The break is due to a QML type diff --git a/src/datavisualization/engine/abstract3drenderer.cpp b/src/datavisualization/engine/abstract3drenderer.cpp index 782b1480..c2ed43da 100644 --- a/src/datavisualization/engine/abstract3drenderer.cpp +++ b/src/datavisualization/engine/abstract3drenderer.cpp @@ -69,6 +69,7 @@ Abstract3DRenderer::Abstract3DRenderer(Abstract3DController *controller) m_graphHorizontalAspectRatio(0.0f), m_polarGraph(false), m_radialLabelOffset(1.0f), + m_polarRadius(2.0f), m_xRightAngleRotation(QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, 90.0f)), m_yRightAngleRotation(QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, 90.0f)), m_zRightAngleRotation(QQuaternion::fromAxisAndAngle(0.0f, 0.0f, 1.0f, 90.0f)), @@ -313,8 +314,6 @@ void Abstract3DRenderer::updateSelectionMode(QAbstract3DGraph::SelectionFlags mo void Abstract3DRenderer::updateAspectRatio(float ratio) { m_graphAspectRatio = ratio; - calculateZoomLevel(); - m_cachedScene->activeCamera()->d_ptr->updateViewMatrix(m_autoScaleAdjustment); foreach (SeriesRenderCache *cache, m_renderCacheList) cache->setDataDirty(true); updateCustomItemPositions(); @@ -369,9 +368,9 @@ void Abstract3DRenderer::calculateZoomLevel() GLfloat div; GLfloat zoomAdjustment; div = qMin(m_primarySubViewport.width(), m_primarySubViewport.height()); - zoomAdjustment = 2.0f * defaultRatio + zoomAdjustment = defaultRatio * ((m_primarySubViewport.width() / div) - / (m_primarySubViewport.height() / div)) / m_graphAspectRatio; + / (m_primarySubViewport.height() / div)); m_autoScaleAdjustment = qMin(zoomAdjustment, 1.0f); // clamp to 1.0f } @@ -1206,8 +1205,8 @@ void Abstract3DRenderer::calculatePolarXZ(const QVector3D &dataPos, float &x, fl qreal radius = m_axisCacheZ.formatter()->positionAt(dataPos.z()); // Convert angle & radius to X and Z coords - x = float(radius * qSin(angle)) * m_graphAspectRatio; - z = -float(radius * qCos(angle)) * m_graphAspectRatio; + x = float(radius * qSin(angle)) * m_polarRadius; + z = -float(radius * qCos(angle)) * m_polarRadius; } void Abstract3DRenderer::drawRadialGrid(ShaderHelper *shader, float yFloorLinePos, @@ -1234,10 +1233,10 @@ void Abstract3DRenderer::drawRadialGrid(ShaderHelper *shader, float yFloorLinePo for (int i = 0; i < gridLineCount; i++) { float gridPosition = (i >= mainSize) ? subGridPositions.at(i - mainSize) : gridPositions.at(i); - float radiusFraction = m_graphAspectRatio * gridPosition; + float radiusFraction = m_polarRadius * gridPosition; QVector3D gridLineScaler(radiusFraction * float(qSin(polarGridHalfAngle)), gridLineWidth, gridLineWidth); - translateVector.setZ(gridPosition * m_graphAspectRatio); + translateVector.setZ(gridPosition * m_polarRadius); for (int j = 0; j < polarGridRoundness; j++) { QMatrix4x4 modelMatrix; QMatrix4x4 itModelMatrix; @@ -1275,7 +1274,7 @@ void Abstract3DRenderer::drawAngularGrid(ShaderHelper *shader, float yFloorLineP const QMatrix4x4 &projectionViewMatrix, const QMatrix4x4 &depthMatrix) { - float halfRatio((m_graphAspectRatio + (labelMargin / 2.0f)) / 2.0f); + float halfRatio((m_polarRadius + (labelMargin / 2.0f)) / 2.0f); QVector3D gridLineScaler(gridLineWidth, gridLineWidth, halfRatio); int gridLineCount = m_axisCacheX.gridLineCount(); const QVector &gridPositions = m_axisCacheX.formatter()->gridPositions(); @@ -1337,10 +1336,10 @@ float Abstract3DRenderer::calculatePolarBackgroundMargin() float actualLabelWidth = actualLabelHeight / labelSize.height() * labelSize.width(); float labelPosition = labelPositions.at(label); qreal angle = labelPosition * M_PI * 2.0; - float x = qAbs((m_graphAspectRatio + labelMargin) * float(qSin(angle))) - + actualLabelWidth - m_graphAspectRatio + labelMargin; - float z = qAbs(-(m_graphAspectRatio + labelMargin) * float(qCos(angle))) - + actualLabelHeight - m_graphAspectRatio + labelMargin; + float x = qAbs((m_polarRadius + labelMargin) * float(qSin(angle))) + + actualLabelWidth - m_polarRadius + labelMargin; + float z = qAbs(-(m_polarRadius + labelMargin) * float(qCos(angle))) + + actualLabelHeight - m_polarRadius + labelMargin; float neededMargin = qMax(x, z); maxNeededMargin = qMax(maxNeededMargin, neededMargin); } diff --git a/src/datavisualization/engine/abstract3drenderer_p.h b/src/datavisualization/engine/abstract3drenderer_p.h index f7fa748b..40c7db0a 100644 --- a/src/datavisualization/engine/abstract3drenderer_p.h +++ b/src/datavisualization/engine/abstract3drenderer_p.h @@ -260,6 +260,7 @@ protected: float m_graphHorizontalAspectRatio; bool m_polarGraph; float m_radialLabelOffset; + float m_polarRadius; QQuaternion m_xRightAngleRotation; QQuaternion m_yRightAngleRotation; diff --git a/src/datavisualization/engine/scatter3drenderer.cpp b/src/datavisualization/engine/scatter3drenderer.cpp index 56219339..76b955e8 100644 --- a/src/datavisualization/engine/scatter3drenderer.cpp +++ b/src/datavisualization/engine/scatter3drenderer.cpp @@ -57,8 +57,8 @@ Scatter3DRenderer::Scatter3DRenderer(Scatter3DController *controller) m_selectionDepthBuffer(0), m_shadowQualityToShader(100.0f), m_shadowQualityMultiplier(3), - m_heightNormalizer(1.0f), m_scaleX(0.0f), + m_scaleY(0.0f), m_scaleZ(0.0f), m_scaleXWithBackground(0.0f), m_scaleYWithBackground(0.0f), @@ -75,9 +75,6 @@ Scatter3DRenderer::Scatter3DRenderer(Scatter3DController *controller) m_haveUniformColorMeshSeries(false), m_haveGradientMeshSeries(false) { - m_axisCacheY.setScale(2.0f); - m_axisCacheY.setTranslate(-1.0f); - initializeOpenGLFunctions(); initializeOpenGL(); } @@ -707,6 +704,9 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) dotShader->bind(); } + float rangeGradientYScaler = 0.5f / m_scaleY; + float rangeGradientYScalerForPoints = rangeGradientYScaler * 100.0f; + foreach (SeriesRenderCache *baseCache, m_renderCacheList) { if (baseCache->isVisible()) { ScatterSeriesRenderCache *cache = @@ -808,7 +808,8 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) if (rangeGradientPoints) { // Drawing points with range gradient // Get color from gradient based on items y position converted to percent - int position = int(item.translation().y() * 50.0f) + 50; + int position = int(item.translation().y() * rangeGradientYScalerForPoints) + + 50; dotColor = Utils::vectorFromColor( cache->gradientImage().pixel(0, position)); } else { @@ -825,13 +826,12 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) else gradientTexture = cache->singleHighlightGradientTexture(); lightStrength = m_cachedTheme->highlightLightStrength(); - // Insert data to ScatterRenderItem - // We don't have ownership, so don't delete the previous one + // Save the reference to the item to be used in label drawing selectedItem = &item; dotSelectionFound = true; // Save selected item size (adjusted with font size) for selection label // positioning - selectedItemSize = itemSize + (m_cachedTheme->font().pointSizeF() / 500.0f); + selectedItemSize = itemSize + m_drawer->scaledFontSize() - 0.05f; } if (!drawingPoints) { @@ -846,7 +846,8 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) dotShader->setUniformValue(dotShader->color(), dotColor); } else if (colorStyle == Q3DTheme::ColorStyleRangeGradient) { dotShader->setUniformValue(dotShader->gradientMin(), - (item.translation().y() + 1.0f) / 2.0f); + (item.translation().y() + m_scaleY) + * rangeGradientYScaler); } #if !defined(QT_OPENGL_ES_2) if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { @@ -925,12 +926,12 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) else gradientTexture = cache->singleHighlightGradientTexture(); GLfloat lightStrength = m_cachedTheme->highlightLightStrength(); - // Save the reference to the item to be used on label drawing + // Save the reference to the item to be used in label drawing selectedItem = &item; dotSelectionFound = true; // Save selected item size (adjusted with font size) for selection label // positioning - selectedItemSize = itemSize + (m_cachedTheme->font().pointSizeF() / 500.0f); + selectedItemSize = itemSize + m_drawer->scaledFontSize() - 0.05f; if (!drawingPoints) { // Set shader bindings @@ -944,7 +945,8 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) dotShader->setUniformValue(dotShader->color(), dotColor); } else if (colorStyle == Q3DTheme::ColorStyleRangeGradient) { dotShader->setUniformValue(dotShader->gradientMin(), - (item.translation().y() + 1.0f) / 2.0f); + (item.translation().y() + m_scaleY) + * rangeGradientYScaler); } if (!drawingPoints) { @@ -1575,7 +1577,7 @@ void Scatter3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa if (m_polarGraph) { float direction = m_zFlipped ? -1.0f : 1.0f; labelTrans.setZ((m_axisCacheZ.formatter()->labelPositions().at(label) - * -m_graphAspectRatio + * -m_polarRadius + m_drawer->scaledFontSize() + gridLineWidth) * direction); } else { labelTrans.setZ(m_axisCacheZ.labelPosition(label)); @@ -1596,7 +1598,7 @@ void Scatter3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa } if (!drawSelection && m_axisCacheZ.isTitleVisible()) { if (m_polarGraph) { - float titleZ = -m_graphAspectRatio / 2.0f; + float titleZ = -m_polarRadius / 2.0f; if (m_zFlipped) titleZ = -titleZ; labelTrans.setZ(titleZ); @@ -1730,8 +1732,8 @@ void Scatter3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa continue; float labelPosition = labelPositions.at(label); qreal angle = labelPosition * M_PI * 2.0; - labelTrans.setX((m_graphAspectRatio + labelMargin) * float(qSin(angle))); - labelTrans.setZ(-(m_graphAspectRatio + labelMargin) * float(qCos(angle))); + labelTrans.setX((m_polarRadius + labelMargin) * float(qSin(angle))); + labelTrans.setZ(-(m_polarRadius + labelMargin) * float(qCos(angle))); // Alignment depends on label angular position, as well as flips Qt::AlignmentFlag vAlignment = Qt::AlignCenter; Qt::AlignmentFlag hAlignment = Qt::AlignCenter; @@ -1776,7 +1778,7 @@ void Scatter3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa totalRotation *= m_zRightAngleRotationNeg; if (m_yFlippedForGrid) totalRotation *= QQuaternion::fromAxisAndAngle(0.0f, 0.0f, 1.0f, -180.0f); - labelTrans.setZ(-m_graphAspectRatio); + labelTrans.setZ(-m_polarRadius); radial = true; } drawAxisTitleX(labelRotation, labelTrans, totalRotation, m_dummyRenderItem, @@ -2022,8 +2024,6 @@ void Scatter3DRenderer::calculateSceneScalingFactors() m_hBackgroundMargin = qMax(m_hBackgroundMargin, polarMargin); } - m_heightNormalizer = GLfloat(m_axisCacheY.max() - m_axisCacheY.min()) / 2.0f; - float horizontalAspectRatio; if (m_polarGraph) horizontalAspectRatio = 1.0f; @@ -2039,19 +2039,31 @@ void Scatter3DRenderer::calculateSceneScalingFactors() areaSize.setWidth(horizontalAspectRatio); } + float horizontalMaxDimension; + if (m_graphAspectRatio > 2.0f) { + horizontalMaxDimension = 2.0f; + m_scaleY = 2.0f / m_graphAspectRatio; + } else { + horizontalMaxDimension = m_graphAspectRatio; + m_scaleY = 1.0f; + } + if (m_polarGraph) + m_polarRadius = horizontalMaxDimension; + float scaleFactor = qMax(areaSize.width(), areaSize.height()); - m_scaleX = m_graphAspectRatio * areaSize.width() / scaleFactor; - m_scaleZ = m_graphAspectRatio * areaSize.height() / scaleFactor; + m_scaleX = horizontalMaxDimension * areaSize.width() / scaleFactor; + m_scaleZ = horizontalMaxDimension * areaSize.height() / scaleFactor; m_scaleXWithBackground = m_scaleX + m_hBackgroundMargin; + m_scaleYWithBackground = m_scaleY + m_vBackgroundMargin; m_scaleZWithBackground = m_scaleZ + m_hBackgroundMargin; - m_scaleYWithBackground = m_vBackgroundMargin + 1.0f; - float factorScaler = 2.0f * m_graphAspectRatio / scaleFactor; - m_axisCacheX.setScale(factorScaler * areaSize.width()); - m_axisCacheZ.setScale(-factorScaler * areaSize.height()); - m_axisCacheX.setTranslate(-m_axisCacheX.scale() / 2.0f); - m_axisCacheZ.setTranslate(-m_axisCacheZ.scale() / 2.0f); + m_axisCacheX.setScale(m_scaleX * 2.0f); + m_axisCacheY.setScale(m_scaleY * 2.0f); + m_axisCacheZ.setScale(m_scaleZ * 2.0f); + m_axisCacheX.setTranslate(-m_scaleX); + m_axisCacheY.setTranslate(-m_scaleY); + m_axisCacheZ.setTranslate(-m_scaleZ); } void Scatter3DRenderer::initShaders(const QString &vertexShader, const QString &fragmentShader) @@ -2237,7 +2249,7 @@ QVector3D Scatter3DRenderer::convertPositionToTranslation(const QVector3D &posit yTrans = m_axisCacheY.positionAt(position.y()); } else { xTrans = position.x() * m_scaleX; - yTrans = position.y(); + yTrans = position.y() * m_scaleY; zTrans = position.z() * m_scaleZ; } return QVector3D(xTrans, yTrans, zTrans); diff --git a/src/datavisualization/engine/scatter3drenderer_p.h b/src/datavisualization/engine/scatter3drenderer_p.h index 645a2a8e..9baad16a 100644 --- a/src/datavisualization/engine/scatter3drenderer_p.h +++ b/src/datavisualization/engine/scatter3drenderer_p.h @@ -67,8 +67,8 @@ private: GLuint m_selectionDepthBuffer; GLfloat m_shadowQualityToShader; GLint m_shadowQualityMultiplier; - float m_heightNormalizer; float m_scaleX; + float m_scaleY; float m_scaleZ; float m_scaleXWithBackground; float m_scaleYWithBackground; diff --git a/src/datavisualization/engine/shaders/surface.frag b/src/datavisualization/engine/shaders/surface.frag index f17dd73e..238e5fed 100644 --- a/src/datavisualization/engine/shaders/surface.frag +++ b/src/datavisualization/engine/shaders/surface.frag @@ -11,9 +11,11 @@ uniform highp vec3 lightPosition_wrld; uniform highp float lightStrength; uniform highp float ambientStrength; uniform highp vec4 lightColor; +uniform highp float gradMin; +uniform highp float gradHeight; void main() { - highp vec2 gradientUV = vec2(0.0, (coords_mdl.y + 1.0) / 2.0); + highp vec2 gradientUV = vec2(0.0, gradMin + coords_mdl.y * gradHeight); highp vec3 materialDiffuseColor = texture2D(textureSampler, gradientUV).xyz; highp vec3 materialAmbientColor = lightColor.rgb * ambientStrength * materialDiffuseColor; highp vec3 materialSpecularColor = lightColor.rgb; diff --git a/src/datavisualization/engine/shaders/surfaceFlat.frag b/src/datavisualization/engine/shaders/surfaceFlat.frag index 748fb3dd..1a0bbdeb 100644 --- a/src/datavisualization/engine/shaders/surfaceFlat.frag +++ b/src/datavisualization/engine/shaders/surfaceFlat.frag @@ -13,9 +13,11 @@ uniform highp vec3 lightPosition_wrld; uniform highp float lightStrength; uniform highp float ambientStrength; uniform highp vec4 lightColor; +uniform highp float gradMin; +uniform highp float gradHeight; void main() { - highp vec2 gradientUV = vec2(0.0, (coords_mdl.y + 1.0) / 2.0); + highp vec2 gradientUV = vec2(0.0, gradMin + coords_mdl.y * gradHeight); highp vec3 materialDiffuseColor = texture2D(textureSampler, gradientUV).xyz; highp vec3 materialAmbientColor = lightColor.rgb * ambientStrength * materialDiffuseColor; highp vec3 materialSpecularColor = lightColor.rgb; diff --git a/src/datavisualization/engine/shaders/surfaceShadowFlat.frag b/src/datavisualization/engine/shaders/surfaceShadowFlat.frag index 0613a40c..4b0dfae0 100644 --- a/src/datavisualization/engine/shaders/surfaceShadowFlat.frag +++ b/src/datavisualization/engine/shaders/surfaceShadowFlat.frag @@ -7,15 +7,17 @@ varying highp vec3 position_wrld; flat varying highp vec3 normal_cmr; varying highp vec3 eyeDirection_cmr; varying highp vec3 lightDirection_cmr; +varying highp vec4 shadowCoord; uniform highp sampler2DShadow shadowMap; uniform sampler2D textureSampler; -varying highp vec4 shadowCoord; uniform highp vec3 lightPosition_wrld; uniform highp float lightStrength; uniform highp float ambientStrength; uniform highp float shadowQuality; uniform highp vec4 lightColor; +uniform highp float gradMin; +uniform highp float gradHeight; highp vec2 poissonDisk[16] = vec2[16](vec2(-0.94201624, -0.39906216), vec2(0.94558609, -0.76890725), @@ -35,7 +37,7 @@ highp vec2 poissonDisk[16] = vec2[16](vec2(-0.94201624, -0.39906216), vec2(0.14383161, -0.14100790)); void main() { - highp vec2 gradientUV = vec2(0.0, (coords_mdl.y + 1.0) / 2.0); + highp vec2 gradientUV = vec2(0.0, gradMin + coords_mdl.y * gradHeight); highp vec3 materialDiffuseColor = texture2D(textureSampler, gradientUV).xyz; highp vec3 materialAmbientColor = lightColor.rgb * ambientStrength * materialDiffuseColor; highp vec3 materialSpecularColor = lightColor.rgb; diff --git a/src/datavisualization/engine/shaders/surfaceShadowNoTex.frag b/src/datavisualization/engine/shaders/surfaceShadowNoTex.frag index 1acf8f69..d6195227 100644 --- a/src/datavisualization/engine/shaders/surfaceShadowNoTex.frag +++ b/src/datavisualization/engine/shaders/surfaceShadowNoTex.frag @@ -5,15 +5,17 @@ varying highp vec3 position_wrld; varying highp vec3 normal_cmr; varying highp vec3 eyeDirection_cmr; varying highp vec3 lightDirection_cmr; +varying highp vec4 shadowCoord; uniform highp sampler2DShadow shadowMap; uniform sampler2D textureSampler; -varying highp vec4 shadowCoord; uniform highp vec3 lightPosition_wrld; uniform highp float lightStrength; uniform highp float ambientStrength; uniform highp float shadowQuality; uniform highp vec4 lightColor; +uniform highp float gradMin; +uniform highp float gradHeight; highp vec2 poissonDisk[16] = vec2[16](vec2(-0.94201624, -0.39906216), vec2(0.94558609, -0.76890725), @@ -33,7 +35,7 @@ highp vec2 poissonDisk[16] = vec2[16](vec2(-0.94201624, -0.39906216), vec2(0.14383161, -0.14100790)); void main() { - highp vec2 gradientUV = vec2(0.0, (coords_mdl.y + 1.0) / 2.0); + highp vec2 gradientUV = vec2(0.0, gradMin + coords_mdl.y * gradHeight); highp vec3 materialDiffuseColor = texture2D(textureSampler, gradientUV).xyz; highp vec3 materialAmbientColor = lightColor.rgb * ambientStrength * materialDiffuseColor; highp vec3 materialSpecularColor = lightColor.rgb; diff --git a/src/datavisualization/engine/shaders/surface_ES2.frag b/src/datavisualization/engine/shaders/surface_ES2.frag index 58d13834..1d1bdc3e 100644 --- a/src/datavisualization/engine/shaders/surface_ES2.frag +++ b/src/datavisualization/engine/shaders/surface_ES2.frag @@ -10,9 +10,11 @@ uniform sampler2D textureSampler; uniform highp float lightStrength; uniform highp float ambientStrength; uniform highp vec4 lightColor; +uniform highp float gradMin; +uniform highp float gradHeight; void main() { - highp vec2 gradientUV = vec2(0.0, (coords_mdl.y + 1.0) / 2.0); + highp vec2 gradientUV = vec2(0.0, gradMin + coords_mdl.y * gradHeight); highp vec3 materialDiffuseColor = texture2D(textureSampler, gradientUV).xyz; highp vec3 materialAmbientColor = lightColor.rgb * ambientStrength * materialDiffuseColor; highp vec3 materialSpecularColor = lightColor.rgb; diff --git a/src/datavisualization/engine/surface3drenderer.cpp b/src/datavisualization/engine/surface3drenderer.cpp index c1a23493..f5f05498 100644 --- a/src/datavisualization/engine/surface3drenderer.cpp +++ b/src/datavisualization/engine/surface3drenderer.cpp @@ -50,6 +50,7 @@ Surface3DRenderer::Surface3DRenderer(Surface3DController *controller) m_labelShader(0), m_heightNormalizer(0.0f), m_scaleX(0.0f), + m_scaleY(0.0f), m_scaleZ(0.0f), m_scaleXWithBackground(0.0f), m_scaleYWithBackground(0.0f), @@ -70,9 +71,6 @@ Surface3DRenderer::Surface3DRenderer(Surface3DController *controller) m_selectionTexturesDirty(false), m_noShadowTexture(0) { - m_axisCacheY.setScale(2.0f); - m_axisCacheY.setTranslate(-1.0f); - // Check if flat feature is supported ShaderHelper tester(this, QStringLiteral(":/shaders/vertexSurfaceFlat"), QStringLiteral(":/shaders/fragmentSurfaceFlat")); @@ -834,7 +832,7 @@ void Surface3DRenderer::drawSlicedScene() glCullFace(GL_BACK); // Grid lines - if (m_cachedTheme->isGridEnabled() && m_heightNormalizer) { + if (m_cachedTheme->isGridEnabled()) { #if !(defined QT_OPENGL_ES_2) ShaderHelper *lineShader = m_backgroundShader; #else @@ -1257,7 +1255,7 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) // Draw the surface if (!m_renderCacheList.isEmpty()) { - // For surface we can see climpses from underneath + // For surface we can see glimpses from underneath glDisable(GL_CULL_FACE); bool drawGrid = false; @@ -1303,10 +1301,24 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) shader->setUniformValue(shader->lightColor(), lightColor); GLuint gradientTexture; - if (cache->colorStyle() == Q3DTheme::ColorStyleUniform) + if (cache->colorStyle() == Q3DTheme::ColorStyleUniform) { gradientTexture = cache->baseUniformTexture(); - else + shader->setUniformValue(shader->gradientMin(), 0.0f); + shader->setUniformValue(shader->gradientHeight(), 0.0f); + } else { gradientTexture = cache->baseGradientTexture(); + if (cache->colorStyle() == Q3DTheme::ColorStyleObjectGradient) { + float objMin = cache->surfaceObject()->minYValue(); + float objMax = cache->surfaceObject()->maxYValue(); + float objRange = objMax - objMin; + shader->setUniformValue(shader->gradientMin(), -(objMin / objRange)); + shader->setUniformValue(shader->gradientHeight(), 1.0f / objRange); + } else { + shader->setUniformValue(shader->gradientMin(), 0.5f); + shader->setUniformValue(shader->gradientHeight(), + 1.0f / (m_scaleY * 2.0f)); + } + } #if !defined(QT_OPENGL_ES_2) if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { @@ -1433,7 +1445,7 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) QVector3D gridLineScaleZ(gridLineWidth, gridLineWidth, m_scaleZWithBackground); QVector3D gridLineScaleY(gridLineWidth, m_scaleYWithBackground, gridLineWidth); - if (m_cachedTheme->isGridEnabled() && m_heightNormalizer) { + if (m_cachedTheme->isGridEnabled()) { #if !(defined QT_OPENGL_ES_2) ShaderHelper *lineShader = m_backgroundShader; #else @@ -1947,7 +1959,7 @@ void Surface3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa if (m_polarGraph) { float direction = m_zFlipped ? -1.0f : 1.0f; labelTrans.setZ((m_axisCacheZ.formatter()->labelPositions().at(label) - * -m_graphAspectRatio + * -m_polarRadius + m_drawer->scaledFontSize() + gridLineWidth) * direction); } else { labelTrans.setZ(m_axisCacheZ.labelPosition(label)); @@ -1968,7 +1980,7 @@ void Surface3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa } if (!drawSelection && m_axisCacheZ.isTitleVisible()) { if (m_polarGraph) { - float titleZ = -m_graphAspectRatio / 2.0f; + float titleZ = -m_polarRadius / 2.0f; if (m_zFlipped) titleZ = -titleZ; labelTrans.setZ(titleZ); @@ -2106,8 +2118,8 @@ void Surface3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa continue; float labelPosition = labelPositions.at(label); qreal angle = labelPosition * M_PI * 2.0; - labelTrans.setX((m_graphAspectRatio + labelMargin) * float(qSin(angle))); - labelTrans.setZ(-(m_graphAspectRatio + labelMargin) * float(qCos(angle))); + labelTrans.setX((m_polarRadius + labelMargin) * float(qSin(angle))); + labelTrans.setZ(-(m_polarRadius + labelMargin) * float(qCos(angle))); // Alignment depends on label angular position, as well as flips Qt::AlignmentFlag vAlignment = Qt::AlignCenter; Qt::AlignmentFlag hAlignment = Qt::AlignCenter; @@ -2152,7 +2164,7 @@ void Surface3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa totalRotation *= m_zRightAngleRotationNeg; if (m_yFlippedForGrid) totalRotation *= QQuaternion::fromAxisAndAngle(0.0f, 0.0f, 1.0f, -180.0f); - labelTrans.setZ(-m_graphAspectRatio); + labelTrans.setZ(-m_polarRadius); radial = true; } drawAxisTitleX(labelRotation, labelTrans, totalRotation, m_dummyRenderItem, @@ -2415,19 +2427,31 @@ void Surface3DRenderer::calculateSceneScalingFactors() areaSize.setWidth(horizontalAspectRatio); } + float horizontalMaxDimension; + if (m_graphAspectRatio > 2.0f) { + horizontalMaxDimension = 2.0f; + m_scaleY = 2.0f / m_graphAspectRatio; + } else { + horizontalMaxDimension = m_graphAspectRatio; + m_scaleY = 1.0f; + } + if (m_polarGraph) + m_polarRadius = horizontalMaxDimension; + float scaleFactor = qMax(areaSize.width(), areaSize.height()); - m_scaleX = m_graphAspectRatio * areaSize.width() / scaleFactor; - m_scaleZ = m_graphAspectRatio * areaSize.height() / scaleFactor; + m_scaleX = horizontalMaxDimension * areaSize.width() / scaleFactor; + m_scaleZ = horizontalMaxDimension * areaSize.height() / scaleFactor; m_scaleXWithBackground = m_scaleX + m_hBackgroundMargin; + m_scaleYWithBackground = m_scaleY + m_vBackgroundMargin; m_scaleZWithBackground = m_scaleZ + m_hBackgroundMargin; - m_scaleYWithBackground = m_vBackgroundMargin + 1.0f; - float factorScaler = 2.0f * m_graphAspectRatio / scaleFactor; - m_axisCacheX.setScale(factorScaler * areaSize.width()); - m_axisCacheZ.setScale(-factorScaler * areaSize.height()); - m_axisCacheX.setTranslate(-m_axisCacheX.scale() / 2.0f); - m_axisCacheZ.setTranslate(-m_axisCacheZ.scale() / 2.0f); + m_axisCacheX.setScale(m_scaleX * 2.0f); + m_axisCacheY.setScale(m_scaleY * 2.0f); + m_axisCacheZ.setScale(m_scaleZ * 2.0f); + m_axisCacheX.setTranslate(-m_scaleX); + m_axisCacheY.setTranslate(-m_scaleY); + m_axisCacheZ.setTranslate(-m_scaleZ); } void Surface3DRenderer::checkFlatSupport(SurfaceSeriesRenderCache *cache) @@ -2848,7 +2872,7 @@ QVector3D Surface3DRenderer::convertPositionToTranslation(const QVector3D &posit yTrans = m_axisCacheY.positionAt(position.y()); } else { xTrans = position.x() * m_scaleX; - yTrans = position.y(); + yTrans = position.y() * m_scaleY; zTrans = position.z() * m_scaleZ; } return QVector3D(xTrans, yTrans, zTrans); diff --git a/src/datavisualization/engine/surface3drenderer_p.h b/src/datavisualization/engine/surface3drenderer_p.h index 7a547042..cb9c49c3 100644 --- a/src/datavisualization/engine/surface3drenderer_p.h +++ b/src/datavisualization/engine/surface3drenderer_p.h @@ -58,6 +58,7 @@ private: ShaderHelper *m_labelShader; float m_heightNormalizer; float m_scaleX; + float m_scaleY; float m_scaleZ; float m_scaleXWithBackground; float m_scaleYWithBackground; diff --git a/src/datavisualization/utils/surfaceobject.cpp b/src/datavisualization/utils/surfaceobject.cpp index f8675912..a98f2f2a 100644 --- a/src/datavisualization/utils/surfaceobject.cpp +++ b/src/datavisualization/utils/surfaceobject.cpp @@ -69,24 +69,14 @@ void SurfaceObject::setUpSmoothData(const QSurfaceDataArray &dataArray, const QR uvs.resize(totalSize); int totalIndex = 0; - AxisRenderCache &xCache = flipXZ ? m_axisCacheZ : m_axisCacheX; - AxisRenderCache &zCache = flipXZ ? m_axisCacheX : m_axisCacheZ; + // Init min and max to ridiculous values + m_minY = 10000000.0; + m_maxY = -10000000.0f; for (int i = 0; i < m_rows; i++) { const QSurfaceDataRow &p = *dataArray.at(i); for (int j = 0; j < m_columns; j++) { - const QSurfaceDataItem &data = p.at(j); - float normalizedX; - float normalizedZ; - if (polar) { - // Slice don't use polar, so don't care about flip - m_renderer->calculatePolarXZ(data.position(), normalizedX, normalizedZ); - } else { - normalizedX = xCache.positionAt(data.x()); - normalizedZ = zCache.positionAt(data.z()); - } - float normalizedY = m_axisCacheY.positionAt(data.y()); - m_vertices[totalIndex] = QVector3D(normalizedX, normalizedY, normalizedZ); + getNormalizedVertex(p.at(j), m_vertices[totalIndex], polar, flipXZ); if (changeGeometry) uvs[totalIndex] = QVector2D(GLfloat(j) * uvX, GLfloat(i) * uvY); totalIndex++; @@ -151,19 +141,8 @@ void SurfaceObject::updateSmoothRow(const QSurfaceDataArray &dataArray, int rowI int p = rowIndex * m_columns; const QSurfaceDataRow &dataRow = *dataArray.at(rowIndex); - for (int j = 0; j < m_columns; j++) { - const QSurfaceDataItem &data = dataRow.at(j); - float normalizedX; - float normalizedZ; - if (polar) { - m_renderer->calculatePolarXZ(data.position(), normalizedX, normalizedZ); - } else { - normalizedX = m_axisCacheX.positionAt(data.x()); - normalizedZ = m_axisCacheZ.positionAt(data.z()); - } - float normalizedY = m_axisCacheY.positionAt(data.y()); - m_vertices[p++] = QVector3D(normalizedX, normalizedY, normalizedZ); - } + for (int j = 0; j < m_columns; j++) + getNormalizedVertex(dataRow.at(j), m_vertices[p++], polar, false); // Create normals int colLimit = m_columns - 1; @@ -214,17 +193,8 @@ void SurfaceObject::updateSmoothItem(const QSurfaceDataArray &dataArray, int row bool polar) { // Update a vertice - const QSurfaceDataItem &data = dataArray.at(row)->at(column); - float normalizedX; - float normalizedZ; - if (polar) { - m_renderer->calculatePolarXZ(data.position(), normalizedX, normalizedZ); - } else { - normalizedX = m_axisCacheX.positionAt(data.x()); - normalizedZ = m_axisCacheZ.positionAt(data.z()); - } - float normalizedY = m_axisCacheY.positionAt(data.y()); - m_vertices[row * m_columns + column] = QVector3D(normalizedX, normalizedY, normalizedZ); + getNormalizedVertex(dataArray.at(row)->at(column), + m_vertices[row * m_columns + column], polar, false); // Create normals int startRow = row; @@ -376,24 +346,14 @@ void SurfaceObject::setUpData(const QSurfaceDataArray &dataArray, const QRect &s int doubleColumns = m_columns * 2 - 2; int rowColLimit = rowLimit * doubleColumns; - AxisRenderCache &xCache = flipXZ ? m_axisCacheZ : m_axisCacheX; - AxisRenderCache &zCache = flipXZ ? m_axisCacheX : m_axisCacheZ; + // Init min and max to ridiculous values + m_minY = 10000000.0; + m_maxY = -10000000.0f; for (int i = 0; i < m_rows; i++) { const QSurfaceDataRow &row = *dataArray.at(i); for (int j = 0; j < m_columns; j++) { - const QSurfaceDataItem &data = row.at(j); - float normalizedX; - float normalizedZ; - if (polar) { - // Slice don't use polar, so don't care about flip - m_renderer->calculatePolarXZ(data.position(), normalizedX, normalizedZ); - } else { - normalizedX = xCache.positionAt(data.x()); - normalizedZ = zCache.positionAt(data.z()); - } - float normalizedY = m_axisCacheY.positionAt(data.y()); - m_vertices[totalIndex] = QVector3D(normalizedX, normalizedY, normalizedZ); + getNormalizedVertex(row.at(j), m_vertices[totalIndex], polar, flipXZ); if (changeGeometry) uvs[totalIndex] = QVector2D(GLfloat(j) * uvX, GLfloat(i) * uvY); @@ -475,18 +435,7 @@ void SurfaceObject::updateCoarseRow(const QSurfaceDataArray &dataArray, int rowI const QSurfaceDataRow &dataRow = *dataArray.at(rowIndex); for (int j = 0; j < m_columns; j++) { - const QSurfaceDataItem &data = dataRow.at(j); - float normalizedX; - float normalizedZ; - if (polar) { - m_renderer->calculatePolarXZ(data.position(), normalizedX, normalizedZ); - } else { - normalizedX = m_axisCacheX.positionAt(data.x()); - normalizedZ = m_axisCacheZ.positionAt(data.z()); - } - float normalizedY = m_axisCacheY.positionAt(data.y()); - m_vertices[p++] = QVector3D(normalizedX, normalizedY, normalizedZ); - + getNormalizedVertex(dataRow.at(j), m_vertices[p++], polar, false); if (j > 0 && j < colLimit) { m_vertices[p] = m_vertices[p - 1]; p++; @@ -528,18 +477,7 @@ void SurfaceObject::updateCoarseItem(const QSurfaceDataArray &dataArray, int row // Update a vertice int p = row * doubleColumns + column * 2 - (column > 0); - const QSurfaceDataItem &data = dataArray.at(row)->at(column); - float normalizedX; - float normalizedZ; - if (polar) { - m_renderer->calculatePolarXZ(data.position(), normalizedX, normalizedZ); - } else { - normalizedX = m_axisCacheX.positionAt(data.x()); - normalizedZ = m_axisCacheZ.positionAt(data.z()); - } - float normalizedY = m_axisCacheY.positionAt(data.y()); - m_vertices[p] = QVector3D(normalizedX, normalizedY, normalizedZ); - p++; + getNormalizedVertex(dataArray.at(row)->at(column), m_vertices[p++], polar, false); if (column > 0 && column < colLimit) m_vertices[p] = m_vertices[p - 1]; @@ -714,6 +652,31 @@ bool SurfaceObject::checkFlipNormal(const QSurfaceDataArray &array) return ascendingX != ascendingZ; } +void SurfaceObject::getNormalizedVertex(const QSurfaceDataItem &data, QVector3D &vertex, + bool polar, bool flipXZ) +{ + float normalizedX; + float normalizedZ; + if (polar) { + // Slice don't use polar, so don't care about flip + m_renderer->calculatePolarXZ(data.position(), normalizedX, normalizedZ); + } else { + if (flipXZ) { + normalizedX = m_axisCacheZ.positionAt(data.x()); + normalizedZ = m_axisCacheX.positionAt(data.z()); + } else { + normalizedX = m_axisCacheX.positionAt(data.x()); + normalizedZ = m_axisCacheZ.positionAt(data.z()); + } + } + float normalizedY = m_axisCacheY.positionAt(data.y()); + m_minY = qMin(normalizedY, m_minY); + m_maxY = qMax(normalizedY, m_maxY); + vertex.setX(normalizedX); + vertex.setY(normalizedY); + vertex.setZ(normalizedZ); +} + GLuint SurfaceObject::gridElementBuf() { if (!m_meshDataLoaded) diff --git a/src/datavisualization/utils/surfaceobject_p.h b/src/datavisualization/utils/surfaceobject_p.h index 54e6dec3..b3ba80e9 100644 --- a/src/datavisualization/utils/surfaceobject_p.h +++ b/src/datavisualization/utils/surfaceobject_p.h @@ -70,6 +70,8 @@ public: GLuint gridIndexCount(); QVector3D vertexAt(int column, int row); void clear(); + float minYValue() const { return m_minY; } + float maxYValue() const { return m_maxY; } private: QVector3D normal(const QVector3D &a, const QVector3D &b, const QVector3D &c, bool flipNormal); @@ -77,6 +79,8 @@ private: const QVector &normals, const GLint *indices, bool changeGeometry); bool checkFlipNormal(const QSurfaceDataArray &array); + inline void getNormalizedVertex(const QSurfaceDataItem &data, QVector3D &vertex, bool polar, + bool flipXZ); private: SurfaceType m_surfaceType; @@ -91,6 +95,8 @@ private: AxisRenderCache &m_axisCacheY; AxisRenderCache &m_axisCacheZ; Surface3DRenderer *m_renderer; + float m_minY; + float m_maxY; }; QT_END_NAMESPACE_DATAVISUALIZATION diff --git a/tests/surfacetest/graphmodifier.cpp b/tests/surfacetest/graphmodifier.cpp index 1f5a2f8f..0bf63a2f 100644 --- a/tests/surfacetest/graphmodifier.cpp +++ b/tests/surfacetest/graphmodifier.cpp @@ -606,19 +606,30 @@ void GraphModifier::adjustZMin(int min) void GraphModifier::gradientPressed() { + static Q3DTheme::ColorStyle colorStyle = Q3DTheme::ColorStyleUniform; + + if (colorStyle == Q3DTheme::ColorStyleRangeGradient) { + colorStyle = Q3DTheme::ColorStyleObjectGradient; + qDebug() << "Color style: ColorStyleObjectGradient"; + } else if (colorStyle == Q3DTheme::ColorStyleObjectGradient) { + colorStyle = Q3DTheme::ColorStyleUniform; + qDebug() << "Color style: ColorStyleUniform"; + } else { + colorStyle = Q3DTheme::ColorStyleRangeGradient; + qDebug() << "Color style: ColorStyleRangeGradient"; + } + QLinearGradient gradient; gradient.setColorAt(0.0, Qt::black); gradient.setColorAt(0.33, Qt::blue); gradient.setColorAt(0.67, Qt::red); gradient.setColorAt(1.0, Qt::yellow); -// m_graph->seriesList().at(0)->setBaseGradient(gradient); -// m_graph->seriesList().at(0)->setSingleHighlightColor(Qt::red); -// m_graph->seriesList().at(0)->setColorStyle(Q3DTheme::ColorStyleRangeGradient); QList gradients; gradients << gradient; m_graph->activeTheme()->setBaseGradients(gradients); - m_graph->activeTheme()->setColorStyle(Q3DTheme::ColorStyleRangeGradient); + m_graph->activeTheme()->setColorStyle(colorStyle); + } void GraphModifier::changeFont(const QFont &font) -- cgit v1.2.3 From 7178b15207c7df560281f7cc42d090845e2b980a Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Wed, 2 Jul 2014 08:49:13 +0300 Subject: Removed common.pri Common.pri was removed because it was misleadingly named. Moved its contents to more logical places. Change-Id: Ic400472c14f24f42c0a1d3c23786b1c05d6d2659 Reviewed-by: Titta Heikkala --- src/datavisualization/axis/axis.pri | 2 ++ src/datavisualization/common.pri | 7 ------- src/datavisualization/data/data.pri | 2 ++ src/datavisualization/datavisualization.pro | 3 +-- src/datavisualization/engine/engine.pri | 2 ++ src/datavisualization/global/global.pri | 2 ++ src/datavisualization/input/input.pri | 2 ++ src/datavisualization/theme/theme.pri | 2 ++ src/datavisualization/utils/utils.pri | 2 ++ 9 files changed, 15 insertions(+), 9 deletions(-) delete mode 100644 src/datavisualization/common.pri diff --git a/src/datavisualization/axis/axis.pri b/src/datavisualization/axis/axis.pri index 0173b597..4c142c91 100644 --- a/src/datavisualization/axis/axis.pri +++ b/src/datavisualization/axis/axis.pri @@ -16,3 +16,5 @@ SOURCES += \ $$PWD/qcategory3daxis.cpp \ $$PWD/qvalue3daxisformatter.cpp \ $$PWD/qlogvalue3daxisformatter.cpp + +INCLUDEPATH += $$PWD diff --git a/src/datavisualization/common.pri b/src/datavisualization/common.pri deleted file mode 100644 index 9a497c2c..00000000 --- a/src/datavisualization/common.pri +++ /dev/null @@ -1,7 +0,0 @@ -INCLUDEPATH += $$PWD/engine \ - $$PWD/global \ - $$PWD/utils \ - $$PWD/axis \ - $$PWD/data \ - $$PWD/input \ - $$PWD/theme diff --git a/src/datavisualization/data/data.pri b/src/datavisualization/data/data.pri index 73f398bf..37a8d084 100644 --- a/src/datavisualization/data/data.pri +++ b/src/datavisualization/data/data.pri @@ -70,3 +70,5 @@ SOURCES += \ $$PWD/customrenderitem.cpp \ $$PWD/qcustom3ditem.cpp \ $$PWD/qcustom3dlabel.cpp + +INCLUDEPATH += $$PWD diff --git a/src/datavisualization/datavisualization.pro b/src/datavisualization/datavisualization.pro index 44f0f60c..692965b1 100644 --- a/src/datavisualization/datavisualization.pro +++ b/src/datavisualization/datavisualization.pro @@ -19,9 +19,8 @@ QMAKE_DOCS = $$PWD/doc/qtdatavisualization.qdocconf load(qt_module) -include($$PWD/common.pri) -include($$PWD/engine/engine.pri) include($$PWD/global/global.pri) +include($$PWD/engine/engine.pri) include($$PWD/utils/utils.pri) include($$PWD/theme/theme.pri) include($$PWD/axis/axis.pri) diff --git a/src/datavisualization/engine/engine.pri b/src/datavisualization/engine/engine.pri index 96fa7fa9..60703e96 100644 --- a/src/datavisualization/engine/engine.pri +++ b/src/datavisualization/engine/engine.pri @@ -57,3 +57,5 @@ SOURCES += $$PWD/qabstract3dgraph.cpp \ RESOURCES += engine/engine.qrc OTHER_FILES += $$PWD/meshes/* $$PWD/shaders/* + +INCLUDEPATH += $$PWD diff --git a/src/datavisualization/global/global.pri b/src/datavisualization/global/global.pri index 00a3eb65..a43ffb20 100644 --- a/src/datavisualization/global/global.pri +++ b/src/datavisualization/global/global.pri @@ -1,3 +1,5 @@ HEADERS += \ $$PWD/qdatavisualizationglobal.h \ $$PWD/datavisualizationglobal_p.h + +INCLUDEPATH += $$PWD diff --git a/src/datavisualization/input/input.pri b/src/datavisualization/input/input.pri index 5a4c4a76..f6b9fd6a 100644 --- a/src/datavisualization/input/input.pri +++ b/src/datavisualization/input/input.pri @@ -10,3 +10,5 @@ SOURCES += \ $$PWD/qabstract3dinputhandler.cpp \ $$PWD/q3dinputhandler.cpp \ $$PWD/qtouch3dinputhandler.cpp + +INCLUDEPATH += $$PWD diff --git a/src/datavisualization/theme/theme.pri b/src/datavisualization/theme/theme.pri index 1de3035a..cf7b434c 100644 --- a/src/datavisualization/theme/theme.pri +++ b/src/datavisualization/theme/theme.pri @@ -6,3 +6,5 @@ HEADERS += \ SOURCES += \ $$PWD/q3dtheme.cpp \ $$PWD/thememanager.cpp + +INCLUDEPATH += $$PWD diff --git a/src/datavisualization/utils/utils.pri b/src/datavisualization/utils/utils.pri index 30bfb156..904407b8 100644 --- a/src/datavisualization/utils/utils.pri +++ b/src/datavisualization/utils/utils.pri @@ -22,3 +22,5 @@ SOURCES += $$PWD/meshloader.cpp \ $$PWD/surfaceobject.cpp \ $$PWD/scatterobjectbufferhelper.cpp \ $$PWD/scatterpointbufferhelper.cpp + +INCLUDEPATH += $$PWD -- cgit v1.2.3 From 1753a58f78dc328405e0aa77252a9e59b356e5af Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Wed, 2 Jul 2014 09:56:07 +0300 Subject: Add qmlspectrogram example documentation Change-Id: I5a325fa17215c486184cdce0bc26cce8d9c755da Reviewed-by: Mika Salmela --- .../qmlspectrogram/doc/src/qmlspectrogram.qdoc | 46 ++++++++++++++++++++-- .../qmlspectrogram/qml/qmlspectrogram/main.qml | 24 +++++++---- .../doc/src/qtdatavisualization-qml-surface3d.qdoc | 5 +++ src/datavisualization/engine/q3dsurface.cpp | 5 +++ 4 files changed, 69 insertions(+), 11 deletions(-) diff --git a/examples/datavisualization/qmlspectrogram/doc/src/qmlspectrogram.qdoc b/examples/datavisualization/qmlspectrogram/doc/src/qmlspectrogram.qdoc index 6ebba93b..ee0018e3 100644 --- a/examples/datavisualization/qmlspectrogram/doc/src/qmlspectrogram.qdoc +++ b/examples/datavisualization/qmlspectrogram/doc/src/qmlspectrogram.qdoc @@ -22,20 +22,58 @@ \ingroup qtdatavisualization_examples \brief Showing spectrogram graph in a QML application. - The Qt Quick 2 surface example demonstrates how to show a polar and cartesian spectrograms + The Qt Quick 2 Spectrogram example demonstrates how to show a polar and cartesian spectrograms and how to utilize orthographic projection to show them in 2D. - TODO!!! - \image qmlspectrogram-example.png + Spectrogram is simply a surface graph with a range gradient used to emphasize the different + values. Typically spectrograms are shown with two dimensional surfaces, which we simulate + with a top down orthographic view of the graph. In this example, the default input handler + is enabled, so you can also rotate the graph to see how it looks in three dimensions. In + a real application you may want to disable the default input handling if you wish to show + only the two dimensional graph. See \l{Qt Quick 2 Custom Input Example} for guidelines on + customizing input handling. + The focus in this example is on showing how to display spectrograms, so the basic functionality is not explained. For more detailed QML example documentation, see \l{Qt Quick 2 Scatter Example}. - \section1 TODO + \section1 Creating a spectrogram + + To create a 2D spectrogram, we define a Surface3D item: \snippet qmlspectrogram/qml/qmlspectrogram/main.qml 0 + The key properties for enabling the 2D effect are + \l{AbstractGraph3D::orthoProjection}{orthoProjection} and + \l{Camera3D::cameraPreset}{scene.activeCamera.cameraPreset}. We remove the perspective by + enabling orthographic projection for the graph, and then we eliminate the Y-dimension by + viewing the graph directly from above: + + \snippet qmlspectrogram/qml/qmlspectrogram/main.qml 1 + + Since this viewpoint causes the horizontal axis grid to be mostly obscured by the surface, + we also specify that the horizontal grid should be drawn on top of the graph: + + \snippet qmlspectrogram/qml/qmlspectrogram/main.qml 2 + + \section1 Polar spectrogram + + Depending on the data, it is sometimes more natural to use a polar graph instead of a cartesian + one. Qt Data Visualization supports this via \l{AbstractGraph3D::polar}{polar} property. + In this example we provide a button to switch between polar and cartesian modes: + + \snippet qmlspectrogram/qml/qmlspectrogram/main.qml 3 + + In the polar mode, the X-axis is converted into the angular polar axis, and the Z-axis is + converted into the radial polar axis. The surface points are recalculated according to new axes. + + The radial axis labels are drawn outside the graph by default, but in this example we want to + draw them right next to the 0 degree angular axis inside the graph, so we define only a tiny + offset for them: + + \snippet qmlspectrogram/qml/qmlspectrogram/main.qml 4 + \section1 Example contents */ diff --git a/examples/datavisualization/qmlspectrogram/qml/qmlspectrogram/main.qml b/examples/datavisualization/qmlspectrogram/qml/qmlspectrogram/main.qml index af16539b..09c1b8af 100644 --- a/examples/datavisualization/qmlspectrogram/qml/qmlspectrogram/main.qml +++ b/examples/datavisualization/qmlspectrogram/qml/qmlspectrogram/main.qml @@ -41,7 +41,6 @@ Window { anchors.top: mainview.top anchors.left: mainview.left - //! [0] ColorGradient { id: surfaceGradient ColorGradientStop { position: 0.0; color: "black" } @@ -50,7 +49,6 @@ Window { ColorGradientStop { position: 0.8; color: "yellow" } ColorGradientStop { position: 1.0; color: "white" } } - //! [0] ValueAxis3D { id: xAxis @@ -91,6 +89,7 @@ Window { font.pointSize: 25 } + //! [0] Surface3D { id: surfaceGraph width: surfaceView.width @@ -98,18 +97,28 @@ Window { shadowQuality: AbstractGraph3D.ShadowQualityNone selectionMode: AbstractGraph3D.SelectionSlice | AbstractGraph3D.SelectionItemAndRow - scene.activeCamera.cameraPreset: Camera3D.CameraPresetDirectlyAbove - scene.activeCamera.zoomLevel: 85 axisX: xAxis axisY: yAxis axisZ: zAxis theme: customTheme + // Remove the perspective and view the graph from top down to achieve 2D effect + //! [1] orthoProjection: true + scene.activeCamera.cameraPreset: Camera3D.CameraPresetDirectlyAbove + //! [1] + + //! [2] flipHorizontalGrid: true - radialLabelOffset: 0.01 // Add little offset so the labels do not overlap + //! [2] + + //! [4] + radialLabelOffset: 0.01 + //! [4] + horizontalAspectRatio: 1 + scene.activeCamera.zoomLevel: 85 Surface3DSeries { id: surfaceSeries @@ -126,6 +135,7 @@ Window { } } } + //! [0] } RowLayout { @@ -135,12 +145,12 @@ Window { anchors.right: parent.right opacity: 0.5 + //! [3] NewButton { id: polarToggle Layout.fillWidth: true Layout.fillHeight: true text: "Switch to polar" - //! [1] onClicked: { if (surfaceGraph.polar === false) { surfaceGraph.polar = true @@ -150,8 +160,8 @@ Window { text = "Switch to polar" } } - //! [1] } + //! [3] NewButton { id: orthoToggle diff --git a/src/datavisualization/doc/src/qtdatavisualization-qml-surface3d.qdoc b/src/datavisualization/doc/src/qtdatavisualization-qml-surface3d.qdoc index 4188e2b1..2e343d38 100644 --- a/src/datavisualization/doc/src/qtdatavisualization-qml-surface3d.qdoc +++ b/src/datavisualization/doc/src/qtdatavisualization-qml-surface3d.qdoc @@ -89,6 +89,11 @@ * \qmlproperty bool Surface3D::flipHorizontalGrid * \since QtDataVisualization 1.2 * + * In some use cases the horizontal axis grid is mostly covered by the surface, so it can be more + * useful to display the horizontal axis grid on top of the graph rather than on the bottom. + * A typical use case for this is showing 2D spectrograms using orthoGraphic projection with + * a top-down viewpoint. + * * If \c{false}, the horizontal axis grid and labels are drawn on the horizontal background * of the graph. * If \c{true}, the horizontal axis grid and labels are drawn on the opposite side of the graph diff --git a/src/datavisualization/engine/q3dsurface.cpp b/src/datavisualization/engine/q3dsurface.cpp index 90b336df..ce3e7f4c 100644 --- a/src/datavisualization/engine/q3dsurface.cpp +++ b/src/datavisualization/engine/q3dsurface.cpp @@ -235,6 +235,11 @@ QSurface3DSeries *Q3DSurface::selectedSeries() const * \property Q3DSurface::flipHorizontalGrid * \since QtDataVisualization 1.2 * + * In some use cases the horizontal axis grid is mostly covered by the surface, so it can be more + * useful to display the horizontal axis grid on top of the graph rather than on the bottom. + * A typical use case for this is showing 2D spectrograms using orthoGraphic projection with + * a top-down viewpoint. + * * If \c{false}, the horizontal axis grid and labels are drawn on the horizontal background * of the graph. * If \c{true}, the horizontal axis grid and labels are drawn on the opposite side of the graph -- cgit v1.2.3 From 6264a2ff1518d374e6150bf584f2ad3d133457dd Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Wed, 2 Jul 2014 12:00:34 +0300 Subject: Fix non-visible selected item drawing in static optimization mode Task-number: QTRD-3207 Change-Id: Ib7101aa66ed4e11b8be61975239e55f7dd9eff10 Reviewed-by: Mika Salmela --- src/datavisualization/engine/scatter3drenderer.cpp | 140 +++++++++++---------- 1 file changed, 71 insertions(+), 69 deletions(-) diff --git a/src/datavisualization/engine/scatter3drenderer.cpp b/src/datavisualization/engine/scatter3drenderer.cpp index 76b955e8..65719b15 100644 --- a/src/datavisualization/engine/scatter3drenderer.cpp +++ b/src/datavisualization/engine/scatter3drenderer.cpp @@ -898,92 +898,94 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) if (!optimizationDefault && selectedSeries && m_selectedItemIndex != Scatter3DController::invalidSelectionIndex()) { ScatterRenderItem &item = renderArray[m_selectedItemIndex]; - ObjectHelper *dotObj = cache->object(); + if (item.isVisible()) { + ObjectHelper *dotObj = cache->object(); - QMatrix4x4 modelMatrix; - QMatrix4x4 itModelMatrix; + QMatrix4x4 modelMatrix; + QMatrix4x4 itModelMatrix; - modelMatrix.translate(item.translation()); - if (!drawingPoints) { - if (!seriesRotation.isIdentity() || !item.rotation().isIdentity()) { - QQuaternion totalRotation = seriesRotation * item.rotation(); - modelMatrix.rotate(totalRotation); - itModelMatrix.rotate(totalRotation); + modelMatrix.translate(item.translation()); + if (!drawingPoints) { + if (!seriesRotation.isIdentity() || !item.rotation().isIdentity()) { + QQuaternion totalRotation = seriesRotation * item.rotation(); + modelMatrix.rotate(totalRotation); + itModelMatrix.rotate(totalRotation); + } + modelMatrix.scale(modelScaler); + itModelMatrix.scale(modelScaler); } - modelMatrix.scale(modelScaler); - itModelMatrix.scale(modelScaler); - } - QMatrix4x4 MVPMatrix; + QMatrix4x4 MVPMatrix; #ifdef SHOW_DEPTH_TEXTURE_SCENE - MVPMatrix = depthProjectionViewMatrix * modelMatrix; + MVPMatrix = depthProjectionViewMatrix * modelMatrix; #else - MVPMatrix = projectionViewMatrix * modelMatrix; + MVPMatrix = projectionViewMatrix * modelMatrix; #endif - if (useColor) - dotColor = cache->singleHighlightColor(); - else - gradientTexture = cache->singleHighlightGradientTexture(); - GLfloat lightStrength = m_cachedTheme->highlightLightStrength(); - // Save the reference to the item to be used in label drawing - selectedItem = &item; - dotSelectionFound = true; - // Save selected item size (adjusted with font size) for selection label - // positioning - selectedItemSize = itemSize + m_drawer->scaledFontSize() - 0.05f; + if (useColor) + dotColor = cache->singleHighlightColor(); + else + gradientTexture = cache->singleHighlightGradientTexture(); + GLfloat lightStrength = m_cachedTheme->highlightLightStrength(); + // Save the reference to the item to be used in label drawing + selectedItem = &item; + dotSelectionFound = true; + // Save selected item size (adjusted with font size) for selection label + // positioning + selectedItemSize = itemSize + m_drawer->scaledFontSize() - 0.05f; - if (!drawingPoints) { - // Set shader bindings - dotShader->setUniformValue(dotShader->model(), modelMatrix); - dotShader->setUniformValue(dotShader->nModel(), - itModelMatrix.inverted().transposed()); - } + if (!drawingPoints) { + // Set shader bindings + dotShader->setUniformValue(dotShader->model(), modelMatrix); + dotShader->setUniformValue(dotShader->nModel(), + itModelMatrix.inverted().transposed()); + } - dotShader->setUniformValue(dotShader->MVP(), MVPMatrix); - if (useColor) { - dotShader->setUniformValue(dotShader->color(), dotColor); - } else if (colorStyle == Q3DTheme::ColorStyleRangeGradient) { - dotShader->setUniformValue(dotShader->gradientMin(), - (item.translation().y() + m_scaleY) - * rangeGradientYScaler); - } + dotShader->setUniformValue(dotShader->MVP(), MVPMatrix); + if (useColor) { + dotShader->setUniformValue(dotShader->color(), dotColor); + } else if (colorStyle == Q3DTheme::ColorStyleRangeGradient) { + dotShader->setUniformValue(dotShader->gradientMin(), + (item.translation().y() + m_scaleY) + * rangeGradientYScaler); + } - if (!drawingPoints) { - glEnable(GL_POLYGON_OFFSET_FILL); - glPolygonOffset(-1.0f, 1.0f); - } + if (!drawingPoints) { + glEnable(GL_POLYGON_OFFSET_FILL); + glPolygonOffset(-1.0f, 1.0f); + } #if !defined(QT_OPENGL_ES_2) - if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { - if (!drawingPoints) { - // Set shadow shader bindings - QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix; - dotShader->setUniformValue(dotShader->depth(), depthMVPMatrix); - dotShader->setUniformValue(dotShader->lightS(), lightStrength / 10.0f); + if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { + if (!drawingPoints) { + // Set shadow shader bindings + QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix; + dotShader->setUniformValue(dotShader->depth(), depthMVPMatrix); + dotShader->setUniformValue(dotShader->lightS(), lightStrength / 10.0f); - // Draw the object - m_drawer->drawObject(dotShader, dotObj, gradientTexture, m_depthTexture); - } else { - // Draw the object - m_drawer->drawPoint(dotShader); - } - } else + // Draw the object + m_drawer->drawObject(dotShader, dotObj, gradientTexture, m_depthTexture); + } else { + // Draw the object + m_drawer->drawPoint(dotShader); + } + } else #endif - { - if (!drawingPoints) { - // Set shadowless shader bindings - dotShader->setUniformValue(dotShader->lightS(), lightStrength); - // Draw the object - m_drawer->drawObject(dotShader, dotObj, gradientTexture); - } else { - // Draw the object - m_drawer->drawPoint(dotShader); + { + if (!drawingPoints) { + // Set shadowless shader bindings + dotShader->setUniformValue(dotShader->lightS(), lightStrength); + // Draw the object + m_drawer->drawObject(dotShader, dotObj, gradientTexture); + } else { + // Draw the object + m_drawer->drawPoint(dotShader); + } } - } - if (!drawingPoints) - glDisable(GL_POLYGON_OFFSET_FILL); + if (!drawingPoints) + glDisable(GL_POLYGON_OFFSET_FILL); + } } } } -- cgit v1.2.3 From 6e4fb232702fa128b5efa3eef5dfaff4870a9fd0 Mon Sep 17 00:00:00 2001 From: Mika Salmela Date: Fri, 4 Jul 2014 09:58:12 +0300 Subject: Texture to surface API for setting a texture to surface. Task-number: QTRD-3232 Change-Id: Icd9de61882b54b9c1fc84a742e49980926ca681d Reviewed-by: Miikka Heikkinen --- src/datavisualization/data/qsurface3dseries.cpp | 55 ++++++++++ src/datavisualization/data/qsurface3dseries.h | 9 ++ src/datavisualization/data/qsurface3dseries_p.h | 3 + src/datavisualization/engine/engine.qrc | 2 + src/datavisualization/engine/scatter3drenderer.cpp | 4 +- .../engine/shaders/surfaceFlat.vert | 3 + .../engine/shaders/surfaceShadowFlat.vert | 3 + .../engine/shaders/surfaceTexturedFlat.frag | 39 +++++++ .../engine/shaders/surfaceTexturedShadowFlat.frag | 72 +++++++++++++ .../engine/surface3dcontroller.cpp | 19 ++++ .../engine/surface3dcontroller_p.h | 7 +- src/datavisualization/engine/surface3drenderer.cpp | 112 +++++++++++++++++---- src/datavisualization/engine/surface3drenderer_p.h | 3 + .../engine/surfaceseriesrendercache.cpp | 7 +- .../engine/surfaceseriesrendercache_p.h | 3 + .../utils/abstractobjecthelper_p.h | 2 +- src/datavisualization/utils/surfaceobject.cpp | 87 +++++++++++++++- src/datavisualization/utils/surfaceobject_p.h | 6 ++ tests/surfacetest/graphmodifier.cpp | 22 ++-- tests/surfacetest/graphmodifier.h | 2 +- tests/surfacetest/main.cpp | 17 +++- tests/surfacetest/mapimage.png | Bin 0 -> 159540 bytes tests/surfacetest/surfacetest.qrc | 1 + 23 files changed, 436 insertions(+), 42 deletions(-) create mode 100644 src/datavisualization/engine/shaders/surfaceTexturedFlat.frag create mode 100644 src/datavisualization/engine/shaders/surfaceTexturedShadowFlat.frag create mode 100644 tests/surfacetest/mapimage.png diff --git a/src/datavisualization/data/qsurface3dseries.cpp b/src/datavisualization/data/qsurface3dseries.cpp index 1107d721..bcfc4042 100644 --- a/src/datavisualization/data/qsurface3dseries.cpp +++ b/src/datavisualization/data/qsurface3dseries.cpp @@ -299,6 +299,54 @@ QSurface3DSeries::DrawFlags QSurface3DSeries::drawMode() const return dptrc()->m_drawMode; } +/*! + * Set the \a texture as a QImage for the surface. To clear the texture, set empty + * QImage as texture. + */ +void QSurface3DSeries::setTexture(const QImage &texture) +{ + if (dptr()->m_texture != texture) { + dptr()->setTexture(texture); + + emit textureChanged(texture); + dptr()->m_textureFile.clear(); + } +} + +QImage QSurface3DSeries::texture() const +{ + return dptrc()->m_texture; +} + +/*! \property QSurface3DSeries::textureFile + * + * Holds the texture file name for the surface texture. To clear the texture, set empty + * file name. + */ +void QSurface3DSeries::setTextureFile(const QString &filename) +{ + if (dptr()->m_textureFile != filename) { + if (filename.isEmpty()) { + setTexture(QImage()); + } else { + QImage image(filename); + if (image.isNull()) { + qWarning() << "Warning: Tried to set invalid image file as surface texture."; + return; + } + setTexture(image); + } + + dptr()->m_textureFile = filename; + emit textureFileChanged(filename); + } +} + +QString QSurface3DSeries::textureFile() const +{ + return dptrc()->m_textureFile; +} + /*! * \internal */ @@ -445,4 +493,11 @@ void QSurface3DSeriesPrivate::setDrawMode(QSurface3DSeries::DrawFlags mode) } } +void QSurface3DSeriesPrivate::setTexture(const QImage &texture) +{ + m_texture = texture; + if (static_cast(m_controller)) + static_cast(m_controller)->updateSurfaceTexture(qptr()); +} + QT_END_NAMESPACE_DATAVISUALIZATION diff --git a/src/datavisualization/data/qsurface3dseries.h b/src/datavisualization/data/qsurface3dseries.h index 64df7202..7051e583 100644 --- a/src/datavisualization/data/qsurface3dseries.h +++ b/src/datavisualization/data/qsurface3dseries.h @@ -35,6 +35,8 @@ class QT_DATAVISUALIZATION_EXPORT QSurface3DSeries : public QAbstract3DSeries Q_PROPERTY(bool flatShadingEnabled READ isFlatShadingEnabled WRITE setFlatShadingEnabled NOTIFY flatShadingEnabledChanged) Q_PROPERTY(bool flatShadingSupported READ isFlatShadingSupported NOTIFY flatShadingSupportedChanged) Q_PROPERTY(DrawFlags drawMode READ drawMode WRITE setDrawMode NOTIFY drawModeChanged) + Q_PROPERTY(QImage texture READ texture WRITE setTexture NOTIFY textureChanged) + Q_PROPERTY(QString textureFile READ textureFile WRITE setTextureFile NOTIFY textureFileChanged) public: enum DrawFlag { @@ -63,12 +65,19 @@ public: bool isFlatShadingSupported() const; + void setTexture(const QImage &texture); + QImage texture() const; + void setTextureFile(const QString &filename); + QString textureFile() const; + signals: void dataProxyChanged(QSurfaceDataProxy *proxy); void selectedPointChanged(const QPoint &position); void flatShadingEnabledChanged(bool enable); void flatShadingSupportedChanged(bool enable); void drawModeChanged(QSurface3DSeries::DrawFlags mode); + void textureChanged(const QImage &image); + void textureFileChanged(const QString &filename); protected: explicit QSurface3DSeries(QSurface3DSeriesPrivate *d, QObject *parent = 0); diff --git a/src/datavisualization/data/qsurface3dseries_p.h b/src/datavisualization/data/qsurface3dseries_p.h index d4cc2820..270a3ad1 100644 --- a/src/datavisualization/data/qsurface3dseries_p.h +++ b/src/datavisualization/data/qsurface3dseries_p.h @@ -48,6 +48,7 @@ public: void setSelectedPoint(const QPoint &position); void setFlatShadingEnabled(bool enabled); void setDrawMode(QSurface3DSeries::DrawFlags mode); + void setTexture(const QImage &texture); private: QSurface3DSeries *qptr(); @@ -55,6 +56,8 @@ private: QPoint m_selectedPoint; bool m_flatShadingEnabled; QSurface3DSeries::DrawFlags m_drawMode; + QImage m_texture; + QString m_textureFile; private: friend class QSurface3DSeries; diff --git a/src/datavisualization/engine/engine.qrc b/src/datavisualization/engine/engine.qrc index 673b6ee0..f788b5df 100644 --- a/src/datavisualization/engine/engine.qrc +++ b/src/datavisualization/engine/engine.qrc @@ -57,5 +57,7 @@ shaders/texture.frag shaders/texture_ES2.frag shaders/texture.vert + shaders/surfaceTexturedShadowFlat.frag + shaders/surfaceTexturedFlat.frag diff --git a/src/datavisualization/engine/scatter3drenderer.cpp b/src/datavisualization/engine/scatter3drenderer.cpp index 65719b15..5f8eede9 100644 --- a/src/datavisualization/engine/scatter3drenderer.cpp +++ b/src/datavisualization/engine/scatter3drenderer.cpp @@ -2062,10 +2062,10 @@ void Scatter3DRenderer::calculateSceneScalingFactors() m_axisCacheX.setScale(m_scaleX * 2.0f); m_axisCacheY.setScale(m_scaleY * 2.0f); - m_axisCacheZ.setScale(m_scaleZ * 2.0f); + m_axisCacheZ.setScale(-m_scaleZ * 2.0f); m_axisCacheX.setTranslate(-m_scaleX); m_axisCacheY.setTranslate(-m_scaleY); - m_axisCacheZ.setTranslate(-m_scaleZ); + m_axisCacheZ.setTranslate(m_scaleZ); } void Scatter3DRenderer::initShaders(const QString &vertexShader, const QString &fragmentShader) diff --git a/src/datavisualization/engine/shaders/surfaceFlat.vert b/src/datavisualization/engine/shaders/surfaceFlat.vert index 102bea78..0f953f4c 100644 --- a/src/datavisualization/engine/shaders/surfaceFlat.vert +++ b/src/datavisualization/engine/shaders/surfaceFlat.vert @@ -4,6 +4,7 @@ attribute highp vec3 vertexPosition_mdl; attribute highp vec3 vertexNormal_mdl; +attribute highp vec2 vertexUV; uniform highp mat4 MVP; uniform highp mat4 V; @@ -11,6 +12,7 @@ uniform highp mat4 M; uniform highp mat4 itM; uniform highp vec3 lightPosition_wrld; +varying highp vec2 UV; varying highp vec3 position_wrld; flat varying highp vec3 normal_cmr; varying highp vec3 eyeDirection_cmr; @@ -26,4 +28,5 @@ void main() { vec3 lightPosition_cmr = vec4(V * vec4(lightPosition_wrld, 1.0)).xyz; lightDirection_cmr = lightPosition_cmr + eyeDirection_cmr; normal_cmr = vec4(V * itM * vec4(vertexNormal_mdl, 0.0)).xyz; + UV = vertexUV; } diff --git a/src/datavisualization/engine/shaders/surfaceShadowFlat.vert b/src/datavisualization/engine/shaders/surfaceShadowFlat.vert index 8da7b196..d80e062e 100644 --- a/src/datavisualization/engine/shaders/surfaceShadowFlat.vert +++ b/src/datavisualization/engine/shaders/surfaceShadowFlat.vert @@ -4,6 +4,7 @@ attribute highp vec3 vertexPosition_mdl; attribute highp vec3 vertexNormal_mdl; +attribute highp vec2 vertexUV; uniform highp mat4 MVP; uniform highp mat4 V; @@ -12,6 +13,7 @@ uniform highp mat4 itM; uniform highp mat4 depthMVP; uniform highp vec3 lightPosition_wrld; +varying highp vec2 UV; varying highp vec3 position_wrld; flat varying highp vec3 normal_cmr; varying highp vec3 eyeDirection_cmr; @@ -34,4 +36,5 @@ void main() { vec3 lightPosition_cmr = vec4(V * vec4(lightPosition_wrld, 1.0)).xyz; lightDirection_cmr = lightPosition_cmr + eyeDirection_cmr; normal_cmr = vec4(V * itM * vec4(vertexNormal_mdl, 0.0)).xyz; + UV = vertexUV; } diff --git a/src/datavisualization/engine/shaders/surfaceTexturedFlat.frag b/src/datavisualization/engine/shaders/surfaceTexturedFlat.frag new file mode 100644 index 00000000..7c654e0c --- /dev/null +++ b/src/datavisualization/engine/shaders/surfaceTexturedFlat.frag @@ -0,0 +1,39 @@ +#version 120 + +#extension GL_EXT_gpu_shader4 : require + +varying highp vec3 coords_mdl; +varying highp vec3 position_wrld; +flat varying highp vec3 normal_cmr; +varying highp vec3 eyeDirection_cmr; +varying highp vec3 lightDirection_cmr; +varying highp vec2 UV; + +uniform sampler2D textureSampler; +uniform highp vec3 lightPosition_wrld; +uniform highp float lightStrength; +uniform highp float ambientStrength; +uniform highp vec4 lightColor; + +void main() { + highp vec3 materialDiffuseColor = texture2D(textureSampler, UV).xyz; + highp vec3 materialAmbientColor = lightColor.rgb * ambientStrength * materialDiffuseColor; + highp vec3 materialSpecularColor = lightColor.rgb; + + highp float distance = length(lightPosition_wrld - position_wrld); + + highp vec3 n = normalize(normal_cmr); + highp vec3 l = normalize(lightDirection_cmr); + highp float cosTheta = clamp(dot(n, l), 0.0, 1.0); + + highp vec3 E = normalize(eyeDirection_cmr); + highp vec3 R = reflect(-l, n); + highp float cosAlpha = clamp(dot(E, R), 0.0, 1.0); + + gl_FragColor.rgb = + materialAmbientColor + + materialDiffuseColor * lightStrength * pow(cosTheta, 2) / distance + + materialSpecularColor * lightStrength * pow(cosAlpha, 10) / distance; + gl_FragColor.a = 1.0; +} + diff --git a/src/datavisualization/engine/shaders/surfaceTexturedShadowFlat.frag b/src/datavisualization/engine/shaders/surfaceTexturedShadowFlat.frag new file mode 100644 index 00000000..421fe342 --- /dev/null +++ b/src/datavisualization/engine/shaders/surfaceTexturedShadowFlat.frag @@ -0,0 +1,72 @@ +#version 120 + +#extension GL_EXT_gpu_shader4 : require + +varying highp vec3 coords_mdl; +varying highp vec3 position_wrld; +flat varying highp vec3 normal_cmr; +varying highp vec3 eyeDirection_cmr; +varying highp vec3 lightDirection_cmr; +varying highp vec2 UV; + +uniform highp sampler2DShadow shadowMap; +uniform sampler2D textureSampler; +varying highp vec4 shadowCoord; +uniform highp vec3 lightPosition_wrld; +uniform highp float lightStrength; +uniform highp float ambientStrength; +uniform highp float shadowQuality; +uniform highp vec4 lightColor; + +highp vec2 poissonDisk[16] = vec2[16](vec2(-0.94201624, -0.39906216), + vec2(0.94558609, -0.76890725), + vec2(-0.094184101, -0.92938870), + vec2(0.34495938, 0.29387760), + vec2(-0.91588581, 0.45771432), + vec2(-0.81544232, -0.87912464), + vec2(-0.38277543, 0.27676845), + vec2(0.97484398, 0.75648379), + vec2(0.44323325, -0.97511554), + vec2(0.53742981, -0.47373420), + vec2(-0.26496911, -0.41893023), + vec2(0.79197514, 0.19090188), + vec2(-0.24188840, 0.99706507), + vec2(-0.81409955, 0.91437590), + vec2(0.19984126, 0.78641367), + vec2(0.14383161, -0.14100790)); + +void main() { + highp vec3 materialDiffuseColor = texture2D(textureSampler, UV).xyz; + highp vec3 materialAmbientColor = lightColor.rgb * ambientStrength * materialDiffuseColor; + highp vec3 materialSpecularColor = lightColor.rgb; + + highp vec3 n = normalize(normal_cmr); + highp vec3 l = normalize(lightDirection_cmr); + highp float cosTheta = clamp(dot(n, l), 0.0, 1.0); + + highp vec3 E = normalize(eyeDirection_cmr); + highp vec3 R = reflect(-l, n); + highp float cosAlpha = clamp(dot(E, R), 0.0, 1.0); + + highp float bias = 0.005 * tan(acos(cosTheta)); + bias = clamp(bias, 0.0, 0.01); + + vec4 shadCoords = shadowCoord; + shadCoords.z -= bias; + + highp float visibility = 0.6; + for (int i = 0; i < 15; i++) { + vec4 shadCoordsPD = shadCoords; + shadCoordsPD.x += cos(poissonDisk[i].x) / shadowQuality; + shadCoordsPD.y += sin(poissonDisk[i].y) / shadowQuality; + visibility += 0.025 * shadow2DProj(shadowMap, shadCoordsPD).r; + } + + gl_FragColor.rgb = + (materialAmbientColor + + materialDiffuseColor * lightStrength * cosTheta + + materialSpecularColor * lightStrength * pow(cosAlpha, 10)); + gl_FragColor.a = 1.0; + gl_FragColor.rgb = visibility * clamp(gl_FragColor.rgb, 0.0, 1.0); +} + diff --git a/src/datavisualization/engine/surface3dcontroller.cpp b/src/datavisualization/engine/surface3dcontroller.cpp index 5293cb0c..a6c086da 100644 --- a/src/datavisualization/engine/surface3dcontroller.cpp +++ b/src/datavisualization/engine/surface3dcontroller.cpp @@ -85,6 +85,12 @@ void Surface3DController::synchDataToRenderer() m_renderer->updateFlipHorizontalGrid(m_flipHorizontalGrid); m_changeTracker.flipHorizontalGridChanged = false; } + + if (m_changeTracker.surfaceTextureChanged) { + m_renderer->updateSurfaceTextures(m_changedTextures); + m_changeTracker.surfaceTextureChanged = false; + m_changedTextures.clear(); + } } void Surface3DController::handleAxisAutoAdjustRangeChangedInOrientation( @@ -146,6 +152,9 @@ void Surface3DController::addSeries(QAbstract3DSeries *series) QSurface3DSeries *surfaceSeries = static_cast(series); if (surfaceSeries->selectedPoint() != invalidSelectionPosition()) setSelectedPoint(surfaceSeries->selectedPoint(), surfaceSeries, false); + + if (!surfaceSeries->texture().isNull()) + updateSurfaceTexture(surfaceSeries); } void Surface3DController::removeSeries(QAbstract3DSeries *series) @@ -450,6 +459,16 @@ void Surface3DController::handleRowsRemoved(int startIndex, int count) emitNeedRender(); } +void Surface3DController::updateSurfaceTexture(QSurface3DSeries *series) +{ + m_changeTracker.surfaceTextureChanged = true; + + if (!m_changedTextures.contains(series)) + m_changedTextures.append(series); + + emitNeedRender(); +} + void Surface3DController::adjustAxisRanges() { QValue3DAxis *valueAxisX = static_cast(m_axisX); diff --git a/src/datavisualization/engine/surface3dcontroller_p.h b/src/datavisualization/engine/surface3dcontroller_p.h index 653f41c3..8bcdea91 100644 --- a/src/datavisualization/engine/surface3dcontroller_p.h +++ b/src/datavisualization/engine/surface3dcontroller_p.h @@ -42,12 +42,14 @@ struct Surface3DChangeBitField { bool rowsChanged : 1; bool itemChanged : 1; bool flipHorizontalGridChanged : 1; + bool surfaceTextureChanged : 1; Surface3DChangeBitField() : selectedPointChanged(true), rowsChanged(false), itemChanged(false), - flipHorizontalGridChanged(true) + flipHorizontalGridChanged(true), + surfaceTextureChanged(true) { } }; @@ -76,6 +78,7 @@ private: QVector m_changedItems; QVector m_changedRows; bool m_flipHorizontalGrid; + QVector m_changedTextures; public: explicit Surface3DController(QRect rect, Q3DScene *scene = 0); @@ -107,6 +110,8 @@ public: void setFlipHorizontalGrid(bool flip); bool flipHorizontalGrid() const; + void updateSurfaceTexture(QSurface3DSeries *series); + public slots: void handleArrayReset(); void handleRowsAdded(int startIndex, int count); diff --git a/src/datavisualization/engine/surface3drenderer.cpp b/src/datavisualization/engine/surface3drenderer.cpp index f5f05498..a30239f7 100644 --- a/src/datavisualization/engine/surface3drenderer.cpp +++ b/src/datavisualization/engine/surface3drenderer.cpp @@ -43,6 +43,8 @@ Surface3DRenderer::Surface3DRenderer(Surface3DController *controller) m_backgroundShader(0), m_surfaceFlatShader(0), m_surfaceSmoothShader(0), + m_surfaceTexturedSmoothShader(0), + m_surfaceTexturedFlatShader(0), m_surfaceGridShader(0), m_surfaceSliceFlatShader(0), m_surfaceSliceSmoothShader(0), @@ -104,6 +106,8 @@ Surface3DRenderer::~Surface3DRenderer() delete m_selectionShader; delete m_surfaceFlatShader; delete m_surfaceSmoothShader; + delete m_surfaceTexturedSmoothShader; + delete m_surfaceTexturedFlatShader; delete m_surfaceGridShader; delete m_surfaceSliceFlatShader; delete m_surfaceSliceSmoothShader; @@ -257,6 +261,33 @@ void Surface3DRenderer::updateSeries(const QList &seriesLis } } +void Surface3DRenderer::updateSurfaceTextures(QVector seriesList) +{ + foreach (QSurface3DSeries *series, seriesList) { + SurfaceSeriesRenderCache *cache = + static_cast(m_renderCacheList.value(series)); + if (cache) { + GLuint oldTexture = cache->surfaceTexture(); + m_textureHelper->deleteTexture(&oldTexture); + cache->setSurfaceTexture(0); + + const QSurface3DSeries *currentSeries = cache->series(); + QSurfaceDataProxy *dataProxy = currentSeries->dataProxy(); + const QSurfaceDataArray &array = *dataProxy->array(); + + if (!series->texture().isNull()) { + cache->setSurfaceTexture(m_textureHelper->create2DTexture( + series->texture(), true, true, true)); + + if (cache->isFlatShadingEnabled()) + cache->surfaceObject()->coarseUVs(array, cache->dataArray()); + else + cache->surfaceObject()->smoothUVs(array, cache->dataArray()); + } + } + } +} + SeriesRenderCache *Surface3DRenderer::createNewCache(QAbstract3DSeries *series) { m_selectionTexturesDirty = true; @@ -1191,6 +1222,7 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) glCullFace(GL_BACK); } #endif + // Draw selection buffer if (!m_cachedIsSlicingActivated && (!m_renderCacheList.isEmpty() || !m_customRenderCache.isEmpty()) @@ -1220,6 +1252,8 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) MVPMatrix = projectionViewMatrix * modelMatrix; m_selectionShader->setUniformValue(m_selectionShader->MVP(), MVPMatrix); + cache->surfaceObject()->activateSurfaceTexture(false); + m_drawer->drawObject(m_selectionShader, cache->surfaceObject(), cache->selectionTexture()); } @@ -1285,8 +1319,13 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) if (cache->surfaceVisible()) { ShaderHelper *shader = m_surfaceFlatShader; - if (!cache->isFlatShadingEnabled()) + if (cache->surfaceTexture()) + shader = m_surfaceTexturedFlatShader; + if (!cache->isFlatShadingEnabled()) { shader = m_surfaceSmoothShader; + if (cache->surfaceTexture()) + shader = m_surfaceTexturedSmoothShader; + } shader->bind(); // Set shader bindings @@ -1300,23 +1339,30 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) m_cachedTheme->ambientLightStrength()); shader->setUniformValue(shader->lightColor(), lightColor); - GLuint gradientTexture; - if (cache->colorStyle() == Q3DTheme::ColorStyleUniform) { - gradientTexture = cache->baseUniformTexture(); - shader->setUniformValue(shader->gradientMin(), 0.0f); - shader->setUniformValue(shader->gradientHeight(), 0.0f); + // Set the surface texturing + cache->surfaceObject()->activateSurfaceTexture(false); + GLuint texture; + if (cache->surfaceTexture()) { + texture = cache->surfaceTexture(); + cache->surfaceObject()->activateSurfaceTexture(true); } else { - gradientTexture = cache->baseGradientTexture(); - if (cache->colorStyle() == Q3DTheme::ColorStyleObjectGradient) { - float objMin = cache->surfaceObject()->minYValue(); - float objMax = cache->surfaceObject()->maxYValue(); - float objRange = objMax - objMin; - shader->setUniformValue(shader->gradientMin(), -(objMin / objRange)); - shader->setUniformValue(shader->gradientHeight(), 1.0f / objRange); + if (cache->colorStyle() == Q3DTheme::ColorStyleUniform) { + texture = cache->baseUniformTexture(); + shader->setUniformValue(shader->gradientMin(), 0.0f); + shader->setUniformValue(shader->gradientHeight(), 0.0f); } else { - shader->setUniformValue(shader->gradientMin(), 0.5f); - shader->setUniformValue(shader->gradientHeight(), - 1.0f / (m_scaleY * 2.0f)); + texture = cache->baseGradientTexture(); + if (cache->colorStyle() == Q3DTheme::ColorStyleObjectGradient) { + float objMin = cache->surfaceObject()->minYValue(); + float objMax = cache->surfaceObject()->maxYValue(); + float objRange = objMax - objMin; + shader->setUniformValue(shader->gradientMin(), -(objMin / objRange)); + shader->setUniformValue(shader->gradientHeight(), 1.0f / objRange); + } else { + shader->setUniformValue(shader->gradientMin(), 0.5f); + shader->setUniformValue(shader->gradientHeight(), + 1.0f / (m_scaleY * 2.0f)); + } } } @@ -1329,7 +1375,7 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) shader->setUniformValue(shader->lightS(), adjustedLightStrength); // Draw the objects - m_drawer->drawObject(shader, cache->surfaceObject(), gradientTexture, + m_drawer->drawObject(shader, cache->surfaceObject(), texture, m_depthModelTexture); } else #endif @@ -1339,7 +1385,7 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) m_cachedTheme->lightStrength()); // Draw the objects - m_drawer->drawObject(shader, cache->surfaceObject(), gradientTexture); + m_drawer->drawObject(shader, cache->surfaceObject(), texture); } } } @@ -2448,10 +2494,10 @@ void Surface3DRenderer::calculateSceneScalingFactors() m_axisCacheX.setScale(m_scaleX * 2.0f); m_axisCacheY.setScale(m_scaleY * 2.0f); - m_axisCacheZ.setScale(m_scaleZ * 2.0f); + m_axisCacheZ.setScale(-m_scaleZ * 2.0f); m_axisCacheX.setTranslate(-m_scaleX); m_axisCacheY.setTranslate(-m_scaleY); - m_axisCacheZ.setTranslate(-m_scaleZ); + m_axisCacheZ.setTranslate(m_scaleZ); } void Surface3DRenderer::checkFlatSupport(SurfaceSeriesRenderCache *cache) @@ -2470,11 +2516,19 @@ void Surface3DRenderer::updateObjects(SurfaceSeriesRenderCache *cache, bool dime QSurfaceDataArray &dataArray = cache->dataArray(); const QRect &sampleSpace = cache->sampleSpace(); + const QSurface3DSeries *currentSeries = cache->series(); + QSurfaceDataProxy *dataProxy = currentSeries->dataProxy(); + const QSurfaceDataArray &array = *dataProxy->array(); + if (cache->isFlatShadingEnabled()) { cache->surfaceObject()->setUpData(dataArray, sampleSpace, dimensionChanged, m_polarGraph); + if (cache->surfaceTexture()) + cache->surfaceObject()->coarseUVs(array, dataArray); } else { cache->surfaceObject()->setUpSmoothData(dataArray, sampleSpace, dimensionChanged, m_polarGraph); + if (cache->surfaceTexture()) + cache->surfaceObject()->smoothUVs(array, dataArray); } } @@ -2736,6 +2790,10 @@ void Surface3DRenderer::initShaders(const QString &vertexShader, const QString & delete m_surfaceFlatShader; if (m_surfaceSmoothShader) delete m_surfaceSmoothShader; + if (m_surfaceTexturedSmoothShader) + delete m_surfaceTexturedSmoothShader; + if (m_surfaceTexturedFlatShader) + delete m_surfaceTexturedFlatShader; if (m_surfaceSliceFlatShader) delete m_surfaceSliceFlatShader; if (m_surfaceSliceSmoothShader) @@ -2745,9 +2803,13 @@ void Surface3DRenderer::initShaders(const QString &vertexShader, const QString & if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { m_surfaceSmoothShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexShadow"), QStringLiteral(":/shaders/fragmentSurfaceShadowNoTex")); + m_surfaceTexturedSmoothShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexShadow"), + QStringLiteral(":/shaders/fragmentShadow")); } else { m_surfaceSmoothShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertex"), QStringLiteral(":/shaders/fragmentSurface")); + m_surfaceTexturedSmoothShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexTexture"), + QStringLiteral(":/shaders/fragmentTexture")); } m_surfaceSliceSmoothShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertex"), QStringLiteral(":/shaders/fragmentSurface")); @@ -2755,9 +2817,13 @@ void Surface3DRenderer::initShaders(const QString &vertexShader, const QString & if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { m_surfaceFlatShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexSurfaceShadowFlat"), QStringLiteral(":/shaders/fragmentSurfaceShadowFlat")); + m_surfaceTexturedFlatShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexSurfaceShadowFlat"), + QStringLiteral(":/shaders/fragmentTexturedSurfaceShadowFlat")); } else { m_surfaceFlatShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexSurfaceFlat"), QStringLiteral(":/shaders/fragmentSurfaceFlat")); + m_surfaceTexturedFlatShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexSurfaceFlat"), + QStringLiteral(":/shaders/fragmentSurfaceTexturedFlat")); } m_surfaceSliceFlatShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexSurfaceFlat"), QStringLiteral(":/shaders/fragmentSurfaceFlat")); @@ -2770,6 +2836,10 @@ void Surface3DRenderer::initShaders(const QString &vertexShader, const QString & QStringLiteral(":/shaders/fragmentSurfaceES2")); m_surfaceFlatShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertex"), QStringLiteral(":/shaders/fragmentSurfaceES2")); + m_surfaceTexturedSmoothShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexTexture"), + QStringLiteral(":/shaders/fragmentTextureES2")); + m_surfaceTexturedFlatShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexTexture"), + QStringLiteral(":/shaders/fragmentTextureES2")); m_surfaceSliceSmoothShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertex"), QStringLiteral(":/shaders/fragmentSurfaceES2")); m_surfaceSliceFlatShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertex"), @@ -2777,9 +2847,11 @@ void Surface3DRenderer::initShaders(const QString &vertexShader, const QString & #endif m_surfaceSmoothShader->initialize(); m_surfaceSliceSmoothShader->initialize(); + m_surfaceTexturedSmoothShader->initialize(); if (m_flatSupported) { m_surfaceFlatShader->initialize(); m_surfaceSliceFlatShader->initialize(); + m_surfaceTexturedFlatShader->initialize(); } } diff --git a/src/datavisualization/engine/surface3drenderer_p.h b/src/datavisualization/engine/surface3drenderer_p.h index cb9c49c3..c276c1f9 100644 --- a/src/datavisualization/engine/surface3drenderer_p.h +++ b/src/datavisualization/engine/surface3drenderer_p.h @@ -51,6 +51,8 @@ private: ShaderHelper *m_backgroundShader; ShaderHelper *m_surfaceFlatShader; ShaderHelper *m_surfaceSmoothShader; + ShaderHelper *m_surfaceTexturedSmoothShader; + ShaderHelper *m_surfaceTexturedFlatShader; ShaderHelper *m_surfaceGridShader; ShaderHelper *m_surfaceSliceFlatShader; ShaderHelper *m_surfaceSliceSmoothShader; @@ -87,6 +89,7 @@ public: void updateData(); void updateSeries(const QList &seriesList); + void updateSurfaceTextures(QVector seriesList); SeriesRenderCache *createNewCache(QAbstract3DSeries *series); void cleanCache(SeriesRenderCache *cache); void updateSelectionMode(QAbstract3DGraph::SelectionFlags mode); diff --git a/src/datavisualization/engine/surfaceseriesrendercache.cpp b/src/datavisualization/engine/surfaceseriesrendercache.cpp index 1cce5288..f1ad752a 100644 --- a/src/datavisualization/engine/surfaceseriesrendercache.cpp +++ b/src/datavisualization/engine/surfaceseriesrendercache.cpp @@ -39,7 +39,8 @@ SurfaceSeriesRenderCache::SurfaceSeriesRenderCache(QAbstract3DSeries *series, m_sliceSelectionPointer(0), m_mainSelectionPointer(0), m_slicePointerActive(false), - m_mainPointerActive(false) + m_mainPointerActive(false), + m_surfaceTexture(0) { } @@ -62,8 +63,10 @@ void SurfaceSeriesRenderCache::populate(bool newSeries) void SurfaceSeriesRenderCache::cleanup(TextureHelper *texHelper) { - if (QOpenGLContext::currentContext()) + if (QOpenGLContext::currentContext()) { texHelper->deleteTexture(&m_selectionTexture); + texHelper->deleteTexture(&m_surfaceTexture); + } delete m_surfaceObj; delete m_sliceSurfaceObj; diff --git a/src/datavisualization/engine/surfaceseriesrendercache_p.h b/src/datavisualization/engine/surfaceseriesrendercache_p.h index b6254a75..4d04149f 100644 --- a/src/datavisualization/engine/surfaceseriesrendercache_p.h +++ b/src/datavisualization/engine/surfaceseriesrendercache_p.h @@ -85,6 +85,8 @@ public: inline bool slicePointerActive() const { return m_slicePointerActive; } inline void setMainPointerActivity(bool activity) { m_mainPointerActive = activity; } inline bool mainPointerActive() const { return m_mainPointerActive; } + inline void setSurfaceTexture(GLuint texture) { m_surfaceTexture = texture; } + inline GLuint surfaceTexture() const { return m_surfaceTexture; } protected: bool m_surfaceVisible; @@ -105,6 +107,7 @@ protected: SelectionPointer *m_mainSelectionPointer; bool m_slicePointerActive; bool m_mainPointerActive; + GLuint m_surfaceTexture; }; QT_END_NAMESPACE_DATAVISUALIZATION diff --git a/src/datavisualization/utils/abstractobjecthelper_p.h b/src/datavisualization/utils/abstractobjecthelper_p.h index c1618909..b12bc474 100644 --- a/src/datavisualization/utils/abstractobjecthelper_p.h +++ b/src/datavisualization/utils/abstractobjecthelper_p.h @@ -42,7 +42,7 @@ public: GLuint vertexBuf(); GLuint normalBuf(); - GLuint uvBuf(); + virtual GLuint uvBuf(); GLuint elementBuf(); GLuint indexCount(); GLuint indicesType(); diff --git a/src/datavisualization/utils/surfaceobject.cpp b/src/datavisualization/utils/surfaceobject.cpp index a98f2f2a..f8dae6f4 100644 --- a/src/datavisualization/utils/surfaceobject.cpp +++ b/src/datavisualization/utils/surfaceobject.cpp @@ -31,8 +31,8 @@ SurfaceObject::SurfaceObject(Surface3DRenderer *renderer) m_axisCacheX(renderer->m_axisCacheX), m_axisCacheY(renderer->m_axisCacheY), m_axisCacheZ(renderer->m_axisCacheZ), - m_renderer(renderer) - + m_renderer(renderer), + m_returnTextureBuffer(false) { m_indicesType = GL_UNSIGNED_INT; initializeOpenGLFunctions(); @@ -41,12 +41,15 @@ SurfaceObject::SurfaceObject(Surface3DRenderer *renderer) glGenBuffers(1, &m_uvbuffer); glGenBuffers(1, &m_elementbuffer); glGenBuffers(1, &m_gridElementbuffer); + glGenBuffers(1, &m_uvTextureBuffer); } SurfaceObject::~SurfaceObject() { - if (QOpenGLContext::currentContext()) + if (QOpenGLContext::currentContext()) { glDeleteBuffers(1, &m_gridElementbuffer); + glDeleteBuffers(1, &m_uvTextureBuffer); + } } void SurfaceObject::setUpSmoothData(const QSurfaceDataArray &dataArray, const QRect &space, @@ -135,6 +138,37 @@ void SurfaceObject::setUpSmoothData(const QSurfaceDataArray &dataArray, const QR createBuffers(m_vertices, uvs, m_normals, 0, changeGeometry); } +void SurfaceObject::smoothUVs(const QSurfaceDataArray &dataArray, + const QSurfaceDataArray &modelArray) +{ + int columns = dataArray.at(0)->size(); + int rows = dataArray.size(); + float xRangeNormalizer = dataArray.at(0)->at(columns - 1).x() - dataArray.at(0)->at(0).x(); + float zRangeNormalizer = dataArray.at(rows - 1)->at(0).z() - dataArray.at(0)->at(0).z(); + float xMin = dataArray.at(0)->at(0).x(); + float zMin = dataArray.at(0)->at(0).z(); + + QVector uvs; + uvs.resize(m_rows * m_columns); + int index = 0; + for (int i = 0; i < m_rows; i++) { + float y = (modelArray.at(i)->at(0).z() - zMin) / zRangeNormalizer; + const QSurfaceDataRow &p = *modelArray.at(i); + for (int j = 0; j < m_columns; j++) { + float x = (p.at(j).x() - xMin) / xRangeNormalizer; + uvs[index] = QVector2D(x, y); + index++; + } + } + + glBindBuffer(GL_ARRAY_BUFFER, m_uvTextureBuffer); + glBufferData(GL_ARRAY_BUFFER, uvs.size() * sizeof(QVector2D), + &uvs.at(0), GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); + + m_returnTextureBuffer = true; +} + void SurfaceObject::updateSmoothRow(const QSurfaceDataArray &dataArray, int rowIndex, bool polar) { // Update vertices @@ -426,6 +460,42 @@ void SurfaceObject::setUpData(const QSurfaceDataArray &dataArray, const QRect &s delete[] indices; } +void SurfaceObject::coarseUVs(const QSurfaceDataArray &dataArray, + const QSurfaceDataArray &modelArray) +{ + int columns = dataArray.at(0)->size(); + int rows = dataArray.size(); + float xRangeNormalizer = dataArray.at(0)->at(columns - 1).x() - dataArray.at(0)->at(0).x(); + float zRangeNormalizer = dataArray.at(rows - 1)->at(0).z() - dataArray.at(0)->at(0).z(); + float xMin = dataArray.at(0)->at(0).x(); + float zMin = dataArray.at(0)->at(0).z(); + + QVector uvs; + uvs.resize(m_rows * m_columns * 2); + int index = 0; + int colLimit = m_columns - 1; + for (int i = 0; i < m_rows; i++) { + float y = (modelArray.at(i)->at(0).z() - zMin) / zRangeNormalizer; + const QSurfaceDataRow &p = *modelArray.at(i); + for (int j = 0; j < m_columns; j++) { + float x = (p.at(j).x() - xMin) / xRangeNormalizer; + uvs[index] = QVector2D(x, y); + index++; + if (j > 0 && j < colLimit) { + uvs[index] = uvs[index - 1]; + index++; + } + } + } + + glBindBuffer(GL_ARRAY_BUFFER, m_uvTextureBuffer); + glBufferData(GL_ARRAY_BUFFER, uvs.size() * sizeof(QVector2D), + &uvs.at(0), GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); + + m_returnTextureBuffer = true; +} + void SurfaceObject::updateCoarseRow(const QSurfaceDataArray &dataArray, int rowIndex, bool polar) { int colLimit = m_columns - 1; @@ -684,6 +754,17 @@ GLuint SurfaceObject::gridElementBuf() return m_gridElementbuffer; } +GLuint SurfaceObject::uvBuf() +{ + if (!m_meshDataLoaded) + qFatal("No loaded object"); + + if (m_returnTextureBuffer) + return m_uvTextureBuffer; + else + return m_uvbuffer; +} + GLuint SurfaceObject::gridIndexCount() { return m_gridIndexCount; diff --git a/src/datavisualization/utils/surfaceobject_p.h b/src/datavisualization/utils/surfaceobject_p.h index b3ba80e9..3266c626 100644 --- a/src/datavisualization/utils/surfaceobject_p.h +++ b/src/datavisualization/utils/surfaceobject_p.h @@ -57,6 +57,8 @@ public: bool changeGeometry, bool polar, bool flipXZ = false); void setUpSmoothData(const QSurfaceDataArray &dataArray, const QRect &space, bool changeGeometry, bool polar, bool flipXZ = false); + void smoothUVs(const QSurfaceDataArray &dataArray, const QSurfaceDataArray &modelArray); + void coarseUVs(const QSurfaceDataArray &dataArray, const QSurfaceDataArray &modelArray); void updateCoarseRow(const QSurfaceDataArray &dataArray, int rowIndex, bool polar); void updateSmoothRow(const QSurfaceDataArray &dataArray, int startRow, bool polar); void updateSmoothItem(const QSurfaceDataArray &dataArray, int row, int column, bool polar); @@ -67,11 +69,13 @@ public: void createCoarseGridlineIndices(int x, int y, int endX, int endY); void uploadBuffers(); GLuint gridElementBuf(); + GLuint uvBuf(); GLuint gridIndexCount(); QVector3D vertexAt(int column, int row); void clear(); float minYValue() const { return m_minY; } float maxYValue() const { return m_maxY; } + inline void activateSurfaceTexture(bool value) { m_returnTextureBuffer = value; } private: QVector3D normal(const QVector3D &a, const QVector3D &b, const QVector3D &c, bool flipNormal); @@ -97,6 +101,8 @@ private: Surface3DRenderer *m_renderer; float m_minY; float m_maxY; + GLuint m_uvTextureBuffer; + bool m_returnTextureBuffer; }; QT_END_NAMESPACE_DATAVISUALIZATION diff --git a/tests/surfacetest/graphmodifier.cpp b/tests/surfacetest/graphmodifier.cpp index 0bf63a2f..a685b71b 100644 --- a/tests/surfacetest/graphmodifier.cpp +++ b/tests/surfacetest/graphmodifier.cpp @@ -48,12 +48,12 @@ GraphModifier::GraphModifier(Q3DSurface *graph) m_zCount(24), m_activeSample(0), m_fontSize(40), - m_rangeX(16.0), + m_rangeX(34.0), m_rangeY(16.0), - m_rangeZ(16.0), - m_minX(-8.0), + m_rangeZ(34.0), + m_minX(-17.0), m_minY(-8.0), - m_minZ(-8.0), + m_minZ(-17.0), m_addRowCounter(m_zCount), m_insertTestZPos(0), m_insertTestIndexPos(1), @@ -1244,12 +1244,12 @@ void GraphModifier::resetArray() void GraphModifier::resetArrayEmpty() { - QSurfaceDataArray *emptryArray = new QSurfaceDataArray; + QSurfaceDataArray *emptyArray = new QSurfaceDataArray; #ifdef MULTI_SERIES int series = rand() % 4; - m_multiseries[series]->dataProxy()->resetArray(emptryArray); + m_multiseries[series]->dataProxy()->resetArray(emptyArray); #else - m_theSeries->dataProxy()->resetArray(emptryArray); + m_theSeries->dataProxy()->resetArray(emptyArray); #endif } @@ -1594,3 +1594,11 @@ void GraphModifier::setHorizontalAspectRatio(int ratio) qreal aspectRatio = qreal(ratio) / 100.0; m_graph->setHorizontalAspectRatio(aspectRatio); } + +void GraphModifier::setSurfaceTexture(bool enabled) +{ + if (enabled) + m_multiseries[3]->setTexture(QImage(":/maps/mapimage")); + else + m_multiseries[3]->setTexture(QImage()); +} diff --git a/tests/surfacetest/graphmodifier.h b/tests/surfacetest/graphmodifier.h index f472c1ec..62145d45 100644 --- a/tests/surfacetest/graphmodifier.h +++ b/tests/surfacetest/graphmodifier.h @@ -112,9 +112,9 @@ public: void massiveTestAppendAndScroll(); void testAxisReverse(); void testDataOrdering(); - void setAspectRatio(int ratio); void setHorizontalAspectRatio(int ratio); + void setSurfaceTexture(bool enabled); public slots: void changeShadowQuality(int quality); diff --git a/tests/surfacetest/main.cpp b/tests/surfacetest/main.cpp index 95d4637e..3a7307ea 100644 --- a/tests/surfacetest/main.cpp +++ b/tests/surfacetest/main.cpp @@ -56,7 +56,7 @@ int main(int argc, char *argv[]) surfaceGraph->activeTheme()->setType(Q3DTheme::Theme(initialTheme)); QWidget *container = QWidget::createWindowContainer(surfaceGraph); - container->setMinimumSize(QSize(screenSize.width() / 4, screenSize.height() / 4)); + container->setMinimumSize(QSize(screenSize.width() / 2, screenSize.height() / 4)); container->setMaximumSize(screenSize); container->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); container->setFocusPolicy(Qt::StrongFocus); @@ -183,7 +183,7 @@ int main(int argc, char *argv[]) QSlider *axisRangeSliderX = new QSlider(Qt::Horizontal, widget); axisRangeSliderX->setTickInterval(1); axisRangeSliderX->setMinimum(1); - axisRangeSliderX->setValue(16); + axisRangeSliderX->setValue(34); axisRangeSliderX->setMaximum(100); axisRangeSliderX->setEnabled(true); QSlider *axisRangeSliderY = new QSlider(Qt::Horizontal, widget); @@ -195,14 +195,14 @@ int main(int argc, char *argv[]) QSlider *axisRangeSliderZ = new QSlider(Qt::Horizontal, widget); axisRangeSliderZ->setTickInterval(1); axisRangeSliderZ->setMinimum(1); - axisRangeSliderZ->setValue(16); + axisRangeSliderZ->setValue(34); axisRangeSliderZ->setMaximum(100); axisRangeSliderZ->setEnabled(true); QSlider *axisMinSliderX = new QSlider(Qt::Horizontal, widget); axisMinSliderX->setTickInterval(1); axisMinSliderX->setMinimum(-100); - axisMinSliderX->setValue(-8); + axisMinSliderX->setValue(-17); axisMinSliderX->setMaximum(100); axisMinSliderX->setEnabled(true); QSlider *axisMinSliderY = new QSlider(Qt::Horizontal, widget); @@ -214,7 +214,7 @@ int main(int argc, char *argv[]) QSlider *axisMinSliderZ = new QSlider(Qt::Horizontal, widget); axisMinSliderZ->setTickInterval(1); axisMinSliderZ->setMinimum(-100); - axisMinSliderZ->setValue(-8); + axisMinSliderZ->setValue(-17); axisMinSliderZ->setMaximum(100); axisMinSliderZ->setEnabled(true); @@ -402,6 +402,10 @@ int main(int argc, char *argv[]) polarCB->setText(QStringLiteral("Polar")); polarCB->setChecked(false); + QCheckBox *surfaceTextureCB = new QCheckBox(widget); + surfaceTextureCB->setText(QStringLiteral("Map texture")); + surfaceTextureCB->setChecked(false); + // Add controls to the layout #ifdef MULTI_SERIES vLayout->addWidget(series1CB); @@ -429,6 +433,7 @@ int main(int argc, char *argv[]) vLayout->addWidget(surfaceGridS4CB); vLayout->addWidget(surfaceS4CB); vLayout->addWidget(series4VisibleCB); + vLayout->addWidget(surfaceTextureCB); #endif #ifndef MULTI_SERIES vLayout->addWidget(new QLabel(QStringLiteral("Select surface sample"))); @@ -669,6 +674,8 @@ int main(int argc, char *argv[]) modifier, &GraphModifier::setAspectRatio); QObject::connect(horizontalAspectRatioSlider, &QSlider::valueChanged, modifier, &GraphModifier::setHorizontalAspectRatio); + QObject::connect(surfaceTextureCB, &QCheckBox::stateChanged, + modifier, &GraphModifier::setSurfaceTexture); #ifdef MULTI_SERIES modifier->setSeries1CB(series1CB); diff --git a/tests/surfacetest/mapimage.png b/tests/surfacetest/mapimage.png new file mode 100644 index 00000000..079d0407 Binary files /dev/null and b/tests/surfacetest/mapimage.png differ diff --git a/tests/surfacetest/surfacetest.qrc b/tests/surfacetest/surfacetest.qrc index c18da2c4..266ed7e0 100644 --- a/tests/surfacetest/surfacetest.qrc +++ b/tests/surfacetest/surfacetest.qrc @@ -1,5 +1,6 @@ Heightmap.png + mapimage.png -- cgit v1.2.3 From 7cb21647f8b44cdae3b5e1ce8803083e641308e2 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Fri, 4 Jul 2014 10:45:50 +0300 Subject: Use default font in spectrogram example Change-Id: I2b16eecfaf5ba866dd07c6593a8b101dbc2cc112 Reviewed-by: Miikka Heikkinen --- examples/datavisualization/qmlspectrogram/qml/qmlspectrogram/main.qml | 2 -- 1 file changed, 2 deletions(-) diff --git a/examples/datavisualization/qmlspectrogram/qml/qmlspectrogram/main.qml b/examples/datavisualization/qmlspectrogram/qml/qmlspectrogram/main.qml index 09c1b8af..b61bf817 100644 --- a/examples/datavisualization/qmlspectrogram/qml/qmlspectrogram/main.qml +++ b/examples/datavisualization/qmlspectrogram/qml/qmlspectrogram/main.qml @@ -85,8 +85,6 @@ Window { lightStrength: 0.0 ambientLightStrength: 0.9 backgroundEnabled: false - font.family: "Lucida Handwriting" - font.pointSize: 25 } //! [0] -- cgit v1.2.3 From eda443e725d94dca2782d48cfd19380257d19ba2 Mon Sep 17 00:00:00 2001 From: Mika Salmela Date: Fri, 4 Jul 2014 10:52:25 +0300 Subject: Arrow blender files backup Change-Id: I293687ebea6565bf56ec7c3089fa236b46de1369 Reviewed-by: Mika Salmela --- tools/blender/arrow.blend | Bin 0 -> 497968 bytes tools/blender/narrowArrow.blend | Bin 0 -> 489112 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 tools/blender/arrow.blend create mode 100644 tools/blender/narrowArrow.blend diff --git a/tools/blender/arrow.blend b/tools/blender/arrow.blend new file mode 100644 index 00000000..9c0ef46b Binary files /dev/null and b/tools/blender/arrow.blend differ diff --git a/tools/blender/narrowArrow.blend b/tools/blender/narrowArrow.blend new file mode 100644 index 00000000..3fd3dff9 Binary files /dev/null and b/tools/blender/narrowArrow.blend differ -- cgit v1.2.3 From 9c3b9fb2d8f07b2b3f2e5bc84653711043c9936c Mon Sep 17 00:00:00 2001 From: Mika Salmela Date: Fri, 4 Jul 2014 11:12:06 +0300 Subject: Remove obsolete fillDepthTexture function Not needed anymore. The m_noShadowTexture takes care of this. Task-number: QTRD-3212 Change-Id: I30d8b6a9cfa7561914c4210cb7334d8ef8a4cca8 Reviewed-by: Miikka Heikkinen --- src/datavisualization/engine/surface3drenderer.cpp | 2 -- src/datavisualization/utils/texturehelper.cpp | 18 ------------------ src/datavisualization/utils/texturehelper_p.h | 1 - 3 files changed, 21 deletions(-) diff --git a/src/datavisualization/engine/surface3drenderer.cpp b/src/datavisualization/engine/surface3drenderer.cpp index a30239f7..96574807 100644 --- a/src/datavisualization/engine/surface3drenderer.cpp +++ b/src/datavisualization/engine/surface3drenderer.cpp @@ -2917,8 +2917,6 @@ void Surface3DRenderer::updateDepthBuffer() m_depthFrameBuffer, m_shadowQualityMultiplier); if (m_depthTexture) { - m_textureHelper->fillDepthTexture(m_depthTexture, m_primarySubViewport.size(), - m_shadowQualityMultiplier, 1.0f); m_depthModelTexture = m_textureHelper->createDepthTexture(m_primarySubViewport.size(), m_shadowQualityMultiplier); } diff --git a/src/datavisualization/utils/texturehelper.cpp b/src/datavisualization/utils/texturehelper.cpp index 97f9c672..616c1981 100644 --- a/src/datavisualization/utils/texturehelper.cpp +++ b/src/datavisualization/utils/texturehelper.cpp @@ -236,24 +236,6 @@ GLuint TextureHelper::createDepthTextureFrameBuffer(const QSize &size, GLuint &f } #endif -#if !defined(QT_OPENGL_ES_2) -void TextureHelper::fillDepthTexture(GLuint texture,const QSize &size, GLuint textureSize, - GLfloat value) -{ - int nItems = size.width() * textureSize * size.height() * textureSize; - GLfloat *bits = new GLfloat[nItems]; - for (int i = 0; i < nItems; i++) - bits[i] = value; - - glBindTexture(GL_TEXTURE_2D, texture); - glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, size.width() * textureSize, - size.height() * textureSize, 0, GL_DEPTH_COMPONENT, GL_FLOAT, bits); - glBindTexture(GL_TEXTURE_2D, 0); - - delete[] bits; -} -#endif - void TextureHelper::deleteTexture(GLuint *texture) { if (texture && *texture) { diff --git a/src/datavisualization/utils/texturehelper_p.h b/src/datavisualization/utils/texturehelper_p.h index aec137a4..c20f293c 100644 --- a/src/datavisualization/utils/texturehelper_p.h +++ b/src/datavisualization/utils/texturehelper_p.h @@ -53,7 +53,6 @@ class TextureHelper : protected QOpenGLFunctions GLuint createDepthTexture(const QSize &size, GLuint textureSize); // Returns depth texture and inserts generated framebuffer to parameter GLuint createDepthTextureFrameBuffer(const QSize &size, GLuint &frameBuffer, GLuint textureSize); - void fillDepthTexture(GLuint texture, const QSize &size, GLuint textureSize, GLfloat value); #endif void deleteTexture(GLuint *texture); -- cgit v1.2.3 From 1b311517f8889e6faa8a1ae51af582c1cd07e739 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Fri, 4 Jul 2014 12:14:13 +0300 Subject: Exposed default input handlers to QML Also added properties to control rotation, zoom, and selection individually. Also added missing documentation about surface texture. Task-number: QTRD-3202 Change-Id: I981edb7f336aea499440559f4a2098711200206d Reviewed-by: Mika Salmela --- .../qmlspectrogram/doc/src/qmlspectrogram.qdoc | 20 ++- .../qmlspectrogram/qml/qmlspectrogram/main.qml | 30 +++- src/datavisualization/data/qsurface3dseries.cpp | 13 +- .../engine/abstract3dcontroller.cpp | 13 +- src/datavisualization/input/q3dinputhandler.cpp | 184 ++++++++++++++++----- src/datavisualization/input/q3dinputhandler.h | 15 ++ src/datavisualization/input/q3dinputhandler_p.h | 8 +- .../input/qtouch3dinputhandler.cpp | 122 +++++++++----- .../datavisualizationqml2_plugin.cpp | 5 + .../datavisualizationqml2_plugin.h | 5 +- tests/barstest/chart.cpp | 17 +- tests/barstest/chart.h | 7 +- tests/barstest/main.cpp | 77 ++++++--- 13 files changed, 382 insertions(+), 134 deletions(-) diff --git a/examples/datavisualization/qmlspectrogram/doc/src/qmlspectrogram.qdoc b/examples/datavisualization/qmlspectrogram/doc/src/qmlspectrogram.qdoc index ee0018e3..0aebdde0 100644 --- a/examples/datavisualization/qmlspectrogram/doc/src/qmlspectrogram.qdoc +++ b/examples/datavisualization/qmlspectrogram/doc/src/qmlspectrogram.qdoc @@ -29,11 +29,8 @@ Spectrogram is simply a surface graph with a range gradient used to emphasize the different values. Typically spectrograms are shown with two dimensional surfaces, which we simulate - with a top down orthographic view of the graph. In this example, the default input handler - is enabled, so you can also rotate the graph to see how it looks in three dimensions. In - a real application you may want to disable the default input handling if you wish to show - only the two dimensional graph. See \l{Qt Quick 2 Custom Input Example} for guidelines on - customizing input handling. + with a top down orthographic view of the graph. To enforce the 2D effect, we disable the + graph rotation via mouse or touch when in the orthographic mode. The focus in this example is on showing how to display spectrograms, so the basic functionality is not explained. For more detailed QML example documentation, @@ -75,5 +72,18 @@ \snippet qmlspectrogram/qml/qmlspectrogram/main.qml 4 + To enforce the 2D effect, graph rotation via user input is disabled when in orthographic mode. + We do this by specifying a new input handler: + + \snippet qmlspectrogram/qml/qmlspectrogram/main.qml 5 + \snippet qmlspectrogram/qml/qmlspectrogram/main.qml 7 + \dots 0 + \snippet qmlspectrogram/qml/qmlspectrogram/main.qml 6 + \dots 0 + + When the projection mode changes, we adjust the value of the + \l{InputHandler3D::rotationEnabled}{rotationEnabled} property of the \c{customInputHandler} + to control the rotation. + \section1 Example contents */ diff --git a/examples/datavisualization/qmlspectrogram/qml/qmlspectrogram/main.qml b/examples/datavisualization/qmlspectrogram/qml/qmlspectrogram/main.qml index b61bf817..8bb458ca 100644 --- a/examples/datavisualization/qmlspectrogram/qml/qmlspectrogram/main.qml +++ b/examples/datavisualization/qmlspectrogram/qml/qmlspectrogram/main.qml @@ -53,7 +53,7 @@ Window { ValueAxis3D { id: xAxis segmentCount: 8 - labelFormat: "%i" + labelFormat: "%i\u00B0" title: "Angle" titleVisible: true titleFixed: false @@ -72,7 +72,7 @@ Window { ValueAxis3D { id: zAxis segmentCount: 5 - labelFormat: "%i" + labelFormat: "%i nm" title: "Radius" titleVisible: true titleFixed: false @@ -87,19 +87,32 @@ Window { backgroundEnabled: false } + + //! [5] + TouchInputHandler3D { + id: customInputHandler + rotationEnabled: false + } + //! [5] + //! [0] + //! [7] Surface3D { + //! [7] id: surfaceGraph width: surfaceView.width height: surfaceView.height shadowQuality: AbstractGraph3D.ShadowQualityNone - selectionMode: AbstractGraph3D.SelectionSlice | AbstractGraph3D.SelectionItemAndRow + selectionMode: AbstractGraph3D.SelectionSlice | AbstractGraph3D.SelectionItemAndColumn axisX: xAxis axisY: yAxis axisZ: zAxis theme: customTheme + //! [6] + inputHandler: customInputHandler + //! [6] // Remove the perspective and view the graph from top down to achieve 2D effect //! [1] @@ -124,6 +137,7 @@ Window { drawMode: Surface3DSeries.DrawSurface baseGradient: surfaceGradient colorStyle: Theme3D.ColorStyleRangeGradient + itemLabelFormat: "(@xLabel, @zLabel): @yLabel" ItemModelSurfaceDataProxy { itemModel: surfaceData.model @@ -172,6 +186,7 @@ Window { xAxis.labelAutoRotation = 30 yAxis.labelAutoRotation = 30 zAxis.labelAutoRotation = 30 + customInputHandler.rotationEnabled = true text = "Switch to orthographic" } else { surfaceGraph.orthoProjection = true; @@ -180,6 +195,7 @@ Window { xAxis.labelAutoRotation = 0 yAxis.labelAutoRotation = 0 zAxis.labelAutoRotation = 0 + customInputHandler.rotationEnabled = false text = "Switch to perspective" } } @@ -238,7 +254,7 @@ Window { anchors.margins: 20 anchors.bottom: parent.bottom anchors.top: buttonLayout.bottom - anchors.left: parent.left + anchors.right: parent.right border.color: "black" border.width: 1 width: 50 @@ -254,21 +270,21 @@ Window { Text { anchors.verticalCenter: legend.bottom - anchors.left: legend.right + anchors.right: legend.left anchors.margins: 2 text: surfaceGraph.axisY.min + "%" } Text { anchors.verticalCenter: legend.verticalCenter - anchors.left: legend.right + anchors.right: legend.left anchors.margins: 2 text: (surfaceGraph.axisY.max + surfaceGraph.axisY.min) / 2 + "%" } Text { anchors.verticalCenter: legend.top - anchors.left: legend.right + anchors.right: legend.left anchors.margins: 2 text: surfaceGraph.axisY.max + "%" } diff --git a/src/datavisualization/data/qsurface3dseries.cpp b/src/datavisualization/data/qsurface3dseries.cpp index bcfc4042..7d4dacfe 100644 --- a/src/datavisualization/data/qsurface3dseries.cpp +++ b/src/datavisualization/data/qsurface3dseries.cpp @@ -140,6 +140,14 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION * Clearing all flags is not allowed. */ +/*! + * \qmlproperty string Surface3DSeries::textureFile + * + * Holds the texture file name for the surface texture. To clear the texture, set empty + * file name. + */ + + /*! * \enum QSurface3DSeries::DrawFlag * @@ -300,6 +308,8 @@ QSurface3DSeries::DrawFlags QSurface3DSeries::drawMode() const } /*! + * \property QSurface3DSeries::texture + * * Set the \a texture as a QImage for the surface. To clear the texture, set empty * QImage as texture. */ @@ -318,7 +328,8 @@ QImage QSurface3DSeries::texture() const return dptrc()->m_texture; } -/*! \property QSurface3DSeries::textureFile +/*! + * \property QSurface3DSeries::textureFile * * Holds the texture file name for the surface texture. To clear the texture, set empty * file name. diff --git a/src/datavisualization/engine/abstract3dcontroller.cpp b/src/datavisualization/engine/abstract3dcontroller.cpp index 1b497490..52ab853d 100644 --- a/src/datavisualization/engine/abstract3dcontroller.cpp +++ b/src/datavisualization/engine/abstract3dcontroller.cpp @@ -75,10 +75,6 @@ Abstract3DController::Abstract3DController(QRect initialViewport, Q3DScene *scen inputHandler = new QTouch3DInputHandler(); inputHandler->d_ptr->m_isDefaultHandler = true; setActiveInputHandler(inputHandler); - connect(inputHandler, &QAbstract3DInputHandler::inputViewChanged, this, - &Abstract3DController::handleInputViewChanged); - connect(inputHandler, &QAbstract3DInputHandler::positionChanged, this, - &Abstract3DController::handleInputPositionChanged); connect(m_scene->d_ptr.data(), &Q3DScenePrivate::needRender, this, &Abstract3DController::emitNeedRender); } @@ -783,8 +779,9 @@ void Abstract3DController::setActiveInputHandler(QAbstract3DInputHandler *inputH m_inputHandlers.removeAll(m_activeInputHandler); delete m_activeInputHandler; } else { - // Disconnect the old input handler from the scene + // Disconnect the old input handler m_activeInputHandler->setScene(0); + QObject::disconnect(m_activeInputHandler, 0, this, 0); } } @@ -796,6 +793,12 @@ void Abstract3DController::setActiveInputHandler(QAbstract3DInputHandler *inputH if (m_activeInputHandler) m_activeInputHandler->setScene(m_scene); + // Connect the input handler + QObject::connect(m_activeInputHandler, &QAbstract3DInputHandler::inputViewChanged, this, + &Abstract3DController::handleInputViewChanged); + QObject::connect(m_activeInputHandler, &QAbstract3DInputHandler::positionChanged, this, + &Abstract3DController::handleInputPositionChanged); + // Notify change of input handler emit activeInputHandlerChanged(m_activeInputHandler); } diff --git a/src/datavisualization/input/q3dinputhandler.cpp b/src/datavisualization/input/q3dinputhandler.cpp index f0044096..e6f3de24 100644 --- a/src/datavisualization/input/q3dinputhandler.cpp +++ b/src/datavisualization/input/q3dinputhandler.cpp @@ -61,6 +61,43 @@ const float rotationSpeed = 100.0f; * \li Closes the secondary view. * \note Secondary view is available only for Q3DBars and Q3DSurface graphs. * \endtable + * + * Rotation, zoom, and selection can each be individually disabled using + * corresponding properties of this class. + */ + +/*! + * \qmltype InputHandler3D + * \inqmlmodule QtDataVisualization + * \since QtDataVisualization 1.2 + * \ingroup datavisualization_qml + * \instantiates Q3DInputHandler + * \brief Basic wheel mouse based input handler. + * + * InputHandler3D is the basic input handler for wheel mouse type of input devices. + * + * See Q3DInputHandler documentation for more details. + */ + +/*! + * \qmlproperty bool InputHandler3D::rotationEnabled + * \since QtDataVisualization 1.2 + * + * This property specifies if this input handler allows graph rotation. + */ + +/*! + * \qmlproperty bool InputHandler3D::zoomEnabled + * \since QtDataVisualization 1.2 + * + * This property specifies if this input handler allows graph zooming. + */ + +/*! + * \qmlproperty bool InputHandler3D::selectionEnabled + * \since QtDataVisualization 1.2 + * + * This property specifies if this input handler allows selection from the graph. */ /*! @@ -92,29 +129,35 @@ void Q3DInputHandler::mousePressEvent(QMouseEvent *event, const QPoint &mousePos Q_UNUSED(mousePos); #else if (Qt::LeftButton == event->button()) { - if (scene()->isSlicingActive()) { - if (scene()->isPointInPrimarySubView(mousePos)) + if (isSelectionEnabled()) { + if (scene()->isSlicingActive()) { + if (scene()->isPointInPrimarySubView(mousePos)) + setInputView(InputViewOnPrimary); + else if (scene()->isPointInSecondarySubView(mousePos)) + setInputView(InputViewOnSecondary); + else + setInputView(InputViewNone); + } else { + // update mouse positions to prevent jumping when releasing or repressing a button + setInputPosition(mousePos); + scene()->setSelectionQueryPosition(mousePos); setInputView(InputViewOnPrimary); - else if (scene()->isPointInSecondarySubView(mousePos)) - setInputView(InputViewOnSecondary); - else - setInputView(InputViewNone); - } else { - // update mouse positions to prevent jumping when releasing or repressing a button - setInputPosition(mousePos); - scene()->setSelectionQueryPosition(mousePos); - setInputView(InputViewOnPrimary); - d_ptr->m_inputState = QAbstract3DInputHandlerPrivate::InputStateSelecting; + d_ptr->m_inputState = QAbstract3DInputHandlerPrivate::InputStateSelecting; + } } } else if (Qt::MiddleButton == event->button()) { - // reset rotations - setInputPosition(QPoint(0, 0)); + if (isRotationEnabled()) { + // reset rotations + setInputPosition(QPoint(0, 0)); + } } else if (Qt::RightButton == event->button()) { - // disable rotating when in slice view - if (!scene()->isSlicingActive()) - d_ptr->m_inputState = QAbstract3DInputHandlerPrivate::InputStateRotating; - // update mouse positions to prevent jumping when releasing or repressing a button - setInputPosition(mousePos); + if (isRotationEnabled()) { + // disable rotating when in slice view + if (!scene()->isSlicingActive()) + d_ptr->m_inputState = QAbstract3DInputHandlerPrivate::InputStateRotating; + // update mouse positions to prevent jumping when releasing or repressing a button + setInputPosition(mousePos); + } } #endif } @@ -148,7 +191,8 @@ void Q3DInputHandler::mouseMoveEvent(QMouseEvent *event, const QPoint &mousePos) #if defined(Q_OS_IOS) Q_UNUSED(mousePos); #else - if (QAbstract3DInputHandlerPrivate::InputStateRotating == d_ptr->m_inputState) { + if (QAbstract3DInputHandlerPrivate::InputStateRotating == d_ptr->m_inputState + && isRotationEnabled()) { // Calculate mouse movement since last frame float xRotation = scene()->activeCamera()->xRotation(); float yRotation = scene()->activeCamera()->yRotation(); @@ -174,29 +218,91 @@ void Q3DInputHandler::mouseMoveEvent(QMouseEvent *event, const QPoint &mousePos) */ void Q3DInputHandler::wheelEvent(QWheelEvent *event) { - // disable zooming if in slice view - if (scene()->isSlicingActive()) - return; - - // Adjust zoom level based on what zoom range we're in. - int zoomLevel = scene()->activeCamera()->zoomLevel(); - if (zoomLevel > oneToOneZoomLevel) - zoomLevel += event->angleDelta().y() / nearZoomRangeDivider; - else if (zoomLevel > halfSizeZoomLevel) - zoomLevel += event->angleDelta().y() / midZoomRangeDivider; - else - zoomLevel += event->angleDelta().y() / farZoomRangeDivider; - if (zoomLevel > maxZoomLevel) - zoomLevel = maxZoomLevel; - else if (zoomLevel < minZoomLevel) - zoomLevel = minZoomLevel; - - scene()->activeCamera()->setZoomLevel(zoomLevel); + if (isZoomEnabled()) { + // disable zooming if in slice view + if (scene()->isSlicingActive()) + return; + + // Adjust zoom level based on what zoom range we're in. + int zoomLevel = scene()->activeCamera()->zoomLevel(); + if (zoomLevel > oneToOneZoomLevel) + zoomLevel += event->angleDelta().y() / nearZoomRangeDivider; + else if (zoomLevel > halfSizeZoomLevel) + zoomLevel += event->angleDelta().y() / midZoomRangeDivider; + else + zoomLevel += event->angleDelta().y() / farZoomRangeDivider; + if (zoomLevel > maxZoomLevel) + zoomLevel = maxZoomLevel; + else if (zoomLevel < minZoomLevel) + zoomLevel = minZoomLevel; + + scene()->activeCamera()->setZoomLevel(zoomLevel); + } +} + +/*! + * \property Q3DInputHandler::rotationEnabled + * \since QtDataVisualization 1.2 + * + * This property specifies if this input handler allows graph rotation. + */ +void Q3DInputHandler::setRotationEnabled(bool enable) +{ + if (d_ptr->m_rotationEnabled != enable) { + d_ptr->m_rotationEnabled = enable; + emit rotationEnabledChanged(enable); + } +} + +bool Q3DInputHandler::isRotationEnabled() const +{ + return d_ptr->m_rotationEnabled; +} + +/*! + * \property Q3DInputHandler::zoomEnabled + * \since QtDataVisualization 1.2 + * + * This property specifies if this input handler allows graph zooming. + */ +void Q3DInputHandler::setZoomEnabled(bool enable) +{ + if (d_ptr->m_zoomEnabled != enable) { + d_ptr->m_zoomEnabled = enable; + emit zoomEnabledChanged(enable); + } +} + +bool Q3DInputHandler::isZoomEnabled() const +{ + return d_ptr->m_zoomEnabled; +} + +/*! + * \property Q3DInputHandler::selectionEnabled + * \since QtDataVisualization 1.2 + * + * This property specifies if this input handler allows selection from the graph. + */ +void Q3DInputHandler::setSelectionEnabled(bool enable) +{ + if (d_ptr->m_selectionEnabled != enable) { + d_ptr->m_selectionEnabled = enable; + emit selectionEnabledChanged(enable); + } +} + +bool Q3DInputHandler::isSelectionEnabled() const +{ + return d_ptr->m_selectionEnabled; } Q3DInputHandlerPrivate::Q3DInputHandlerPrivate(Q3DInputHandler *q) : q_ptr(q), - m_inputState(QAbstract3DInputHandlerPrivate::InputStateNone) + m_inputState(QAbstract3DInputHandlerPrivate::InputStateNone), + m_rotationEnabled(true), + m_zoomEnabled(true), + m_selectionEnabled(true) { } diff --git a/src/datavisualization/input/q3dinputhandler.h b/src/datavisualization/input/q3dinputhandler.h index 118bd829..9afeb945 100644 --- a/src/datavisualization/input/q3dinputhandler.h +++ b/src/datavisualization/input/q3dinputhandler.h @@ -28,17 +28,32 @@ class Q3DInputHandlerPrivate; class QT_DATAVISUALIZATION_EXPORT Q3DInputHandler : public QAbstract3DInputHandler { Q_OBJECT + Q_PROPERTY(bool rotationEnabled READ isRotationEnabled WRITE setRotationEnabled NOTIFY rotationEnabledChanged) + Q_PROPERTY(bool zoomEnabled READ isZoomEnabled WRITE setZoomEnabled NOTIFY zoomEnabledChanged) + Q_PROPERTY(bool selectionEnabled READ isSelectionEnabled WRITE setSelectionEnabled NOTIFY selectionEnabledChanged) public: explicit Q3DInputHandler(QObject *parent = 0); virtual ~Q3DInputHandler(); + void setRotationEnabled(bool enable); + bool isRotationEnabled() const; + void setZoomEnabled(bool enable); + bool isZoomEnabled() const; + void setSelectionEnabled(bool enable); + bool isSelectionEnabled() const; + // Input event listeners virtual void mousePressEvent(QMouseEvent *event, const QPoint &mousePos); virtual void mouseReleaseEvent(QMouseEvent *event, const QPoint &mousePos); virtual void mouseMoveEvent(QMouseEvent *event, const QPoint &mousePos); virtual void wheelEvent(QWheelEvent *event); +signals: + void rotationEnabledChanged(bool enable); + void zoomEnabledChanged(bool enable); + void selectionEnabledChanged(bool enable); + private: Q_DISABLE_COPY(Q3DInputHandler) diff --git a/src/datavisualization/input/q3dinputhandler_p.h b/src/datavisualization/input/q3dinputhandler_p.h index a9b27307..a3ed8c9c 100644 --- a/src/datavisualization/input/q3dinputhandler_p.h +++ b/src/datavisualization/input/q3dinputhandler_p.h @@ -40,9 +40,15 @@ public: Q3DInputHandlerPrivate(Q3DInputHandler *q); ~Q3DInputHandlerPrivate(); -public: +protected: Q3DInputHandler *q_ptr; QAbstract3DInputHandlerPrivate::InputState m_inputState; + + bool m_rotationEnabled; + bool m_zoomEnabled; + bool m_selectionEnabled; + + friend class Q3DInputHandler; }; QT_END_NAMESPACE_DATAVISUALIZATION diff --git a/src/datavisualization/input/qtouch3dinputhandler.cpp b/src/datavisualization/input/qtouch3dinputhandler.cpp index 30f31d4a..5d62922b 100644 --- a/src/datavisualization/input/qtouch3dinputhandler.cpp +++ b/src/datavisualization/input/qtouch3dinputhandler.cpp @@ -66,6 +66,22 @@ const int maxZoomLevel = 500; * \li Closes the secondary view. * \note Secondary view is available only for Q3DBars and Q3DSurface graphs. * \endtable + * + * Rotation, zoom, and selection can each be individually disabled using + * corresponding Q3DInputHandler properties. + */ + +/*! + * \qmltype TouchInputHandler3D + * \inqmlmodule QtDataVisualization + * \since QtDataVisualization 1.2 + * \ingroup datavisualization_qml + * \instantiates QTouch3DInputHandler + * \brief Basic touch display based input handler. + * + * TouchInputHandler3D is the basic input handler for touch screen devices. + * + * See QTouch3DInputHandler documentation for more details. */ /*! @@ -104,29 +120,38 @@ void QTouch3DInputHandler::touchEvent(QTouchEvent *event) // Flush input state d_ptr->m_inputState = QAbstract3DInputHandlerPrivate::InputStateNone; if (scene()->isSlicingActive()) { - if (scene()->isPointInPrimarySubView(pointerPos.toPoint())) - setInputView(InputViewOnPrimary); - else if (scene()->isPointInSecondarySubView(pointerPos.toPoint())) - setInputView(InputViewOnSecondary); - else - setInputView(InputViewNone); + if (isSelectionEnabled()) { + if (scene()->isPointInPrimarySubView(pointerPos.toPoint())) + setInputView(InputViewOnPrimary); + else if (scene()->isPointInSecondarySubView(pointerPos.toPoint())) + setInputView(InputViewOnSecondary); + else + setInputView(InputViewNone); + } } else { // Handle possible tap-and-hold selection - d_ptr->m_startHoldPos = pointerPos; - d_ptr->m_touchHoldPos = d_ptr->m_startHoldPos; - d_ptr->m_holdTimer->start(); - setInputView(InputViewOnPrimary); + if (isSelectionEnabled()) { + d_ptr->m_startHoldPos = pointerPos; + d_ptr->m_touchHoldPos = d_ptr->m_startHoldPos; + d_ptr->m_holdTimer->start(); + setInputView(InputViewOnPrimary); + } // Start rotating - d_ptr->m_inputState = QAbstract3DInputHandlerPrivate::InputStateRotating; - setInputPosition(pointerPos.toPoint()); + if (isRotationEnabled()) { + d_ptr->m_inputState = QAbstract3DInputHandlerPrivate::InputStateRotating; + setInputPosition(pointerPos.toPoint()); + setInputView(InputViewOnPrimary); + } } } else if (event->type() == QEvent::TouchEnd) { setInputView(InputViewNone); d_ptr->m_holdTimer->stop(); // Handle possible selection if (!scene()->isSlicingActive() - && QAbstract3DInputHandlerPrivate::InputStatePinching != d_ptr->m_inputState) + && QAbstract3DInputHandlerPrivate::InputStatePinching + != d_ptr->m_inputState) { d_ptr->handleSelection(pointerPos); + } } else if (event->type() == QEvent::TouchUpdate) { if (!scene()->isSlicingActive()) { d_ptr->m_touchHoldPos = pointerPos; @@ -158,52 +183,59 @@ QTouch3DInputHandlerPrivate::~QTouch3DInputHandlerPrivate() void QTouch3DInputHandlerPrivate::handlePinchZoom(float distance) { - int newDistance = distance; - int prevDist = q_ptr->prevDistance(); - if (prevDist > 0 && qAbs(prevDist - newDistance) < maxPinchJitter) - return; - m_inputState = QAbstract3DInputHandlerPrivate::InputStatePinching; - Q3DCamera *camera = q_ptr->scene()->activeCamera(); - int zoomLevel = camera->zoomLevel(); - float zoomRate = qSqrt(qSqrt(zoomLevel)); - if (newDistance > prevDist) - zoomLevel += zoomRate; - else - zoomLevel -= zoomRate; - if (zoomLevel > maxZoomLevel) - zoomLevel = maxZoomLevel; - else if (zoomLevel < minZoomLevel) - zoomLevel = minZoomLevel; - camera->setZoomLevel(zoomLevel); - q_ptr->setPrevDistance(newDistance); + if (q_ptr->isZoomEnabled()) { + int newDistance = distance; + int prevDist = q_ptr->prevDistance(); + if (prevDist > 0 && qAbs(prevDist - newDistance) < maxPinchJitter) + return; + m_inputState = QAbstract3DInputHandlerPrivate::InputStatePinching; + Q3DCamera *camera = q_ptr->scene()->activeCamera(); + int zoomLevel = camera->zoomLevel(); + float zoomRate = qSqrt(qSqrt(zoomLevel)); + if (newDistance > prevDist) + zoomLevel += zoomRate; + else + zoomLevel -= zoomRate; + if (zoomLevel > maxZoomLevel) + zoomLevel = maxZoomLevel; + else if (zoomLevel < minZoomLevel) + zoomLevel = minZoomLevel; + camera->setZoomLevel(zoomLevel); + q_ptr->setPrevDistance(newDistance); + } } void QTouch3DInputHandlerPrivate::handleTapAndHold() { - QPointF distance = m_startHoldPos - m_touchHoldPos; - if (distance.manhattanLength() < maxTapAndHoldJitter) { - q_ptr->setInputPosition(m_touchHoldPos.toPoint()); - q_ptr->scene()->setSelectionQueryPosition(m_touchHoldPos.toPoint()); - m_inputState = QAbstract3DInputHandlerPrivate::InputStateSelecting; + if (q_ptr->isSelectionEnabled()) { + QPointF distance = m_startHoldPos - m_touchHoldPos; + if (distance.manhattanLength() < maxTapAndHoldJitter) { + q_ptr->setInputPosition(m_touchHoldPos.toPoint()); + q_ptr->scene()->setSelectionQueryPosition(m_touchHoldPos.toPoint()); + m_inputState = QAbstract3DInputHandlerPrivate::InputStateSelecting; + } } } void QTouch3DInputHandlerPrivate::handleSelection(const QPointF &position) { - QPointF distance = m_startHoldPos - position; - if (distance.manhattanLength() < maxSelectionJitter) { - m_inputState = QAbstract3DInputHandlerPrivate::InputStateSelecting; - q_ptr->scene()->setSelectionQueryPosition(position.toPoint()); - } else { - m_inputState = QAbstract3DInputHandlerPrivate::InputStateNone; - q_ptr->setInputView(QAbstract3DInputHandler::InputViewNone); + if (q_ptr->isSelectionEnabled()) { + QPointF distance = m_startHoldPos - position; + if (distance.manhattanLength() < maxSelectionJitter) { + m_inputState = QAbstract3DInputHandlerPrivate::InputStateSelecting; + q_ptr->scene()->setSelectionQueryPosition(position.toPoint()); + } else { + m_inputState = QAbstract3DInputHandlerPrivate::InputStateNone; + q_ptr->setInputView(QAbstract3DInputHandler::InputViewNone); + } + q_ptr->setPreviousInputPos(position.toPoint()); } - q_ptr->setPreviousInputPos(position.toPoint()); } void QTouch3DInputHandlerPrivate::handleRotation(const QPointF &position) { - if (QAbstract3DInputHandlerPrivate::InputStateRotating == m_inputState) { + if (q_ptr->isRotationEnabled() + && QAbstract3DInputHandlerPrivate::InputStateRotating == m_inputState) { Q3DScene *scene = q_ptr->scene(); Q3DCamera *camera = scene->activeCamera(); float xRotation = camera->xRotation(); diff --git a/src/datavisualizationqml2/datavisualizationqml2_plugin.cpp b/src/datavisualizationqml2/datavisualizationqml2_plugin.cpp index 6e6d9b1c..c824afec 100644 --- a/src/datavisualizationqml2/datavisualizationqml2_plugin.cpp +++ b/src/datavisualizationqml2/datavisualizationqml2_plugin.cpp @@ -93,6 +93,7 @@ void QtDataVisualizationQml2Plugin::registerTypes(const char *uri) QLatin1String("Trying to create uncreatable: Abstract3DSeries.")); qmlRegisterUncreatableType(uri, 1, 1, "AbstractGraph3D", QLatin1String("Trying to create uncreatable: AbstractGraph3D.")); + qmlRegisterType(uri, 1, 1, "ValueAxis3D"); qmlRegisterType(uri, 1, 1, "ItemModelBarDataProxy"); qmlRegisterType(uri, 1, 1, "ItemModelSurfaceDataProxy"); @@ -113,6 +114,10 @@ void QtDataVisualizationQml2Plugin::registerTypes(const char *uri) qmlRegisterUncreatableType(uri, 1, 2, "AbstractGraph3D", QLatin1String("Trying to create uncreatable: AbstractGraph3D.")); qmlRegisterType(uri, 1, 2, "Surface3D"); + + // New types + qmlRegisterType(uri, 1, 2, "InputHandler3D"); + qmlRegisterType(uri, 1, 2, "TouchInputHandler3D"); } QT_END_NAMESPACE_DATAVISUALIZATION diff --git a/src/datavisualizationqml2/datavisualizationqml2_plugin.h b/src/datavisualizationqml2/datavisualizationqml2_plugin.h index 21ef85b8..8d2be659 100644 --- a/src/datavisualizationqml2/datavisualizationqml2_plugin.h +++ b/src/datavisualizationqml2/datavisualizationqml2_plugin.h @@ -42,7 +42,8 @@ #include "declarativeseries_p.h" #include "q3dtheme.h" #include "declarativetheme_p.h" -#include "qabstract3dinputhandler.h" +#include "q3dinputhandler.h" +#include "qtouch3dinputhandler.h" #include "declarativecolor_p.h" #include "declarativescene_p.h" #include "qcustom3ditem.h" @@ -97,6 +98,8 @@ QML_DECLARE_TYPE(Q3DTheme) QML_DECLARE_TYPE(DeclarativeTheme3D) QML_DECLARE_TYPE(QAbstract3DInputHandler) +QML_DECLARE_TYPE(Q3DInputHandler) +QML_DECLARE_TYPE(QTouch3DInputHandler) QML_DECLARE_TYPE(QCustom3DItem) QML_DECLARE_TYPE(QCustom3DLabel) diff --git a/tests/barstest/chart.cpp b/tests/barstest/chart.cpp index d3c38d85..e0f9ff18 100644 --- a/tests/barstest/chart.cpp +++ b/tests/barstest/chart.cpp @@ -203,7 +203,7 @@ GraphModifier::GraphModifier(Q3DBars *barchart, QColorDialog *colorDialog) m_graph->activeTheme()->setFont(QFont("Times Roman", 20)); // Release and store the default input handler. - m_defaultInputHandler = m_graph->activeInputHandler(); + m_defaultInputHandler = static_cast(m_graph->activeInputHandler()); m_graph->releaseInputHandler(m_defaultInputHandler); m_graph->setActiveInputHandler(m_defaultInputHandler); @@ -1406,6 +1406,21 @@ void GraphModifier::reverseValueAxis(int enabled) m_graph->valueAxis()->setReversed(enabled); } +void GraphModifier::setInputHandlerRotationEnabled(int enabled) +{ + m_defaultInputHandler->setRotationEnabled(enabled); +} + +void GraphModifier::setInputHandlerZoomEnabled(int enabled) +{ + m_defaultInputHandler->setZoomEnabled(enabled); +} + +void GraphModifier::setInputHandlerSelectionEnabled(int enabled) +{ + m_defaultInputHandler->setSelectionEnabled(enabled); +} + void GraphModifier::changeValueAxisSegments(int value) { qDebug() << __FUNCTION__ << value; diff --git a/tests/barstest/chart.h b/tests/barstest/chart.h index 47d29c25..7ee781ad 100644 --- a/tests/barstest/chart.h +++ b/tests/barstest/chart.h @@ -20,7 +20,7 @@ #define CHARTMODIFIER_H #include -#include +#include #include #include #include @@ -94,6 +94,9 @@ public: void addRemoveSeries(); void testItemAndRowChanges(); void reverseValueAxis(int enabled); + void setInputHandlerRotationEnabled(int enabled); + void setInputHandlerZoomEnabled(int enabled); + void setInputHandlerSelectionEnabled(int enabled); public slots: void flipViews(); @@ -159,7 +162,7 @@ private: QValue3DAxis *m_currentAxis; bool m_negativeValuesOn; bool m_useNullInputHandler; - QAbstract3DInputHandler *m_defaultInputHandler; + Q3DInputHandler *m_defaultInputHandler; Q3DTheme *m_ownTheme; Q3DTheme *m_builtinTheme; QTimer m_insertRemoveTimer; diff --git a/tests/barstest/main.cpp b/tests/barstest/main.cpp index 5ecf63a4..2d755c7f 100644 --- a/tests/barstest/main.cpp +++ b/tests/barstest/main.cpp @@ -43,6 +43,7 @@ int main(int argc, char **argv) QHBoxLayout *hLayout = new QHBoxLayout(widget); QVBoxLayout *vLayout = new QVBoxLayout(); QVBoxLayout *vLayout2 = new QVBoxLayout(); + QVBoxLayout *vLayout3 = new QVBoxLayout(); // For testing custom surface format QSurfaceFormat surfaceFormat; @@ -65,6 +66,7 @@ int main(int argc, char **argv) hLayout->addWidget(container, 1); hLayout->addLayout(vLayout); hLayout->addLayout(vLayout2); + hLayout->addLayout(vLayout3); QPushButton *addSeriesButton = new QPushButton(widget); addSeriesButton->setText(QStringLiteral("Add / Remove a series")); @@ -220,6 +222,18 @@ int main(int argc, char **argv) staticCheckBox->setText("Use dynamic data"); staticCheckBox->setChecked(false); + QCheckBox *inputHandlerRotationCheckBox = new QCheckBox(widget); + inputHandlerRotationCheckBox->setText("IH: Allow rotation"); + inputHandlerRotationCheckBox->setChecked(true); + + QCheckBox *inputHandlerZoomCheckBox = new QCheckBox(widget); + inputHandlerZoomCheckBox->setText("IH: Allow zoom"); + inputHandlerZoomCheckBox->setChecked(true); + + QCheckBox *inputHandlerSelectionCheckBox = new QCheckBox(widget); + inputHandlerSelectionCheckBox->setText("IH: Allow selection"); + inputHandlerSelectionCheckBox->setChecked(true); + QSlider *rotationSliderX = new QSlider(Qt::Horizontal, widget); rotationSliderX->setTickInterval(1); rotationSliderX->setMinimum(-180); @@ -340,15 +354,15 @@ int main(int argc, char **argv) vLayout->addWidget(insertRemoveTestButton, 0, Qt::AlignTop); vLayout->addWidget(releaseAxesButton, 0, Qt::AlignTop); vLayout->addWidget(releaseProxiesButton, 1, Qt::AlignTop); - vLayout->addWidget(flipViewsButton, 0, Qt::AlignTop); - vLayout->addWidget(changeColorStyleButton, 0, Qt::AlignTop); - vLayout->addWidget(ownThemeButton, 0, Qt::AlignTop); - vLayout->addWidget(primarySeriesTestsButton, 0, Qt::AlignTop); - vLayout->addWidget(toggleRotationButton, 0, Qt::AlignTop); - vLayout->addWidget(gradientBtoYPB, 0, Qt::AlignTop); - vLayout->addWidget(logAxisButton, 0, Qt::AlignTop); - vLayout->addWidget(testItemAndRowChangesButton, 1, Qt::AlignTop); + vLayout2->addWidget(flipViewsButton, 0, Qt::AlignTop); + vLayout2->addWidget(changeColorStyleButton, 0, Qt::AlignTop); + vLayout2->addWidget(ownThemeButton, 0, Qt::AlignTop); + vLayout2->addWidget(primarySeriesTestsButton, 0, Qt::AlignTop); + vLayout2->addWidget(toggleRotationButton, 0, Qt::AlignTop); + vLayout2->addWidget(gradientBtoYPB, 0, Qt::AlignTop); + vLayout2->addWidget(logAxisButton, 0, Qt::AlignTop); + vLayout2->addWidget(testItemAndRowChangesButton, 0, Qt::AlignTop); vLayout2->addWidget(staticCheckBox, 0, Qt::AlignTop); vLayout2->addWidget(rotationCheckBox, 0, Qt::AlignTop); vLayout2->addWidget(rotationSliderX, 0, Qt::AlignTop); @@ -365,24 +379,28 @@ int main(int argc, char **argv) vLayout2->addWidget(minSliderX, 0, Qt::AlignTop); vLayout2->addWidget(minSliderZ, 0, Qt::AlignTop); vLayout2->addWidget(minSliderY, 0, Qt::AlignTop); - vLayout2->addWidget(maxSliderY, 0, Qt::AlignTop); - vLayout2->addWidget(fpsLabel, 0, Qt::AlignTop); - vLayout2->addWidget(fpsCheckBox, 0, Qt::AlignTop); - vLayout2->addWidget(reverseValueAxisCheckBox, 0, Qt::AlignTop); - vLayout2->addWidget(backgroundCheckBox, 0, Qt::AlignTop); - vLayout2->addWidget(gridCheckBox, 0, Qt::AlignTop); - vLayout2->addWidget(new QLabel(QStringLiteral("Adjust shadow quality")), 0, Qt::AlignTop); - vLayout2->addWidget(shadowQuality, 0, Qt::AlignTop); - vLayout2->addWidget(new QLabel(QStringLiteral("Change font")), 0, Qt::AlignTop); - vLayout2->addWidget(fontList, 0, Qt::AlignTop); - vLayout2->addWidget(new QLabel(QStringLiteral("Adjust font size")), 0, Qt::AlignTop); - vLayout2->addWidget(fontSizeSlider, 0, Qt::AlignTop); - vLayout2->addWidget(new QLabel(QStringLiteral("Value axis format")), 0, Qt::AlignTop); - vLayout2->addWidget(valueAxisFormatEdit, 0, Qt::AlignTop); - vLayout2->addWidget(new QLabel(QStringLiteral("Log axis base")), 0, Qt::AlignTop); - vLayout2->addWidget(logBaseEdit, 0, Qt::AlignTop); - vLayout2->addWidget(new QLabel(QStringLiteral("Value axis segments")), 0, Qt::AlignTop); - vLayout2->addWidget(valueAxisSegmentsSpin, 0, Qt::AlignTop); + vLayout2->addWidget(maxSliderY, 1, Qt::AlignTop); + + vLayout3->addWidget(fpsLabel, 0, Qt::AlignTop); + vLayout3->addWidget(fpsCheckBox, 0, Qt::AlignTop); + vLayout3->addWidget(reverseValueAxisCheckBox, 0, Qt::AlignTop); + vLayout3->addWidget(backgroundCheckBox, 0, Qt::AlignTop); + vLayout3->addWidget(gridCheckBox, 0, Qt::AlignTop); + vLayout3->addWidget(inputHandlerRotationCheckBox, 0, Qt::AlignTop); + vLayout3->addWidget(inputHandlerZoomCheckBox, 0, Qt::AlignTop); + vLayout3->addWidget(inputHandlerSelectionCheckBox, 0, Qt::AlignTop); + vLayout3->addWidget(new QLabel(QStringLiteral("Adjust shadow quality")), 0, Qt::AlignTop); + vLayout3->addWidget(shadowQuality, 0, Qt::AlignTop); + vLayout3->addWidget(new QLabel(QStringLiteral("Change font")), 0, Qt::AlignTop); + vLayout3->addWidget(fontList, 0, Qt::AlignTop); + vLayout3->addWidget(new QLabel(QStringLiteral("Adjust font size")), 0, Qt::AlignTop); + vLayout3->addWidget(fontSizeSlider, 0, Qt::AlignTop); + vLayout3->addWidget(new QLabel(QStringLiteral("Value axis format")), 0, Qt::AlignTop); + vLayout3->addWidget(valueAxisFormatEdit, 0, Qt::AlignTop); + vLayout3->addWidget(new QLabel(QStringLiteral("Log axis base")), 0, Qt::AlignTop); + vLayout3->addWidget(logBaseEdit, 0, Qt::AlignTop); + vLayout3->addWidget(new QLabel(QStringLiteral("Value axis segments")), 0, Qt::AlignTop); + vLayout3->addWidget(valueAxisSegmentsSpin, 1, Qt::AlignTop); // TODO: Add example for setMeshFileName widget->show(); @@ -488,7 +506,12 @@ int main(int argc, char **argv) &GraphModifier::setBackgroundEnabled); QObject::connect(gridCheckBox, &QCheckBox::stateChanged, modifier, &GraphModifier::setGridEnabled); - + QObject::connect(inputHandlerRotationCheckBox, &QCheckBox::stateChanged, modifier, + &GraphModifier::setInputHandlerRotationEnabled); + QObject::connect(inputHandlerZoomCheckBox, &QCheckBox::stateChanged, modifier, + &GraphModifier::setInputHandlerZoomEnabled); + QObject::connect(inputHandlerSelectionCheckBox, &QCheckBox::stateChanged, modifier, + &GraphModifier::setInputHandlerSelectionEnabled); QObject::connect(rotationCheckBox, &QCheckBox::stateChanged, modifier, &GraphModifier::setUseNullInputHandler); -- cgit v1.2.3 From 2ab620245c71555e343d21baf1b7f4dcc34d9a9f Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Fri, 4 Jul 2014 14:40:23 +0300 Subject: Improved the visuals in slice modes. Grid lines should no longer vanish into white background due to excessive ambient light multiplier when using high ambient with no specular. I'm still not happy with how bars slice mode colors look. The colors seem washed out compared to main view. Also, on bars, some specular is needed to make bars look 3D, but too much makes it ugly. Also fixes some misc issues in surface slice mode. Task-number: QTRD-3237 Change-Id: I4bed3d922cbc8f5191cc01ab3396849db767cae9 Reviewed-by: Mika Salmela --- .../qmlspectrogram/qml/qmlspectrogram/main.qml | 4 +- src/datavisualization/engine/bars3drenderer.cpp | 9 ++-- src/datavisualization/engine/surface3drenderer.cpp | 55 +++++++++++++++++----- 3 files changed, 52 insertions(+), 16 deletions(-) diff --git a/examples/datavisualization/qmlspectrogram/qml/qmlspectrogram/main.qml b/examples/datavisualization/qmlspectrogram/qml/qmlspectrogram/main.qml index 8bb458ca..4c77fd45 100644 --- a/examples/datavisualization/qmlspectrogram/qml/qmlspectrogram/main.qml +++ b/examples/datavisualization/qmlspectrogram/qml/qmlspectrogram/main.qml @@ -83,8 +83,10 @@ Window { type: Theme3D.ThemeQt // Don't show specular spotlight as we don't want it to distort the colors lightStrength: 0.0 - ambientLightStrength: 0.9 + ambientLightStrength: 1.0 backgroundEnabled: false + gridLineColor: "#AAAAAA" + windowColor: "#EEEEEE" } diff --git a/src/datavisualization/engine/bars3drenderer.cpp b/src/datavisualization/engine/bars3drenderer.cpp index 0b4d5b25..cd6c1eb0 100644 --- a/src/datavisualization/engine/bars3drenderer.cpp +++ b/src/datavisualization/engine/bars3drenderer.cpp @@ -493,7 +493,8 @@ void Bars3DRenderer::drawSlicedScene() lineShader->setUniformValue(lineShader->view(), viewMatrix); lineShader->setUniformValue(lineShader->color(), lineColor); lineShader->setUniformValue(lineShader->ambientS(), - m_cachedTheme->ambientLightStrength() * 2.3f); + m_cachedTheme->ambientLightStrength() + + m_cachedTheme->lightStrength() / 7.0f); lineShader->setUniformValue(lineShader->lightS(), 0.0f); lineShader->setUniformValue(lineShader->lightColor(), lightColor); @@ -601,14 +602,16 @@ void Bars3DRenderer::drawSlicedScene() m_barShader->setUniformValue(m_barShader->view(), viewMatrix); m_barShader->setUniformValue(m_barShader->lightS(), 0.15f); m_barShader->setUniformValue(m_barShader->ambientS(), - m_cachedTheme->ambientLightStrength() * 2.3f); + m_cachedTheme->ambientLightStrength() + + m_cachedTheme->lightStrength() / 7.0f); m_barShader->setUniformValue(m_barShader->lightColor(), lightColor); m_barGradientShader->bind(); m_barGradientShader->setUniformValue(m_barGradientShader->lightP(), lightPos); m_barGradientShader->setUniformValue(m_barGradientShader->view(), viewMatrix); m_barGradientShader->setUniformValue(m_barGradientShader->lightS(), 0.15f); m_barGradientShader->setUniformValue(m_barGradientShader->ambientS(), - m_cachedTheme->ambientLightStrength() * 2.3f); + m_cachedTheme->ambientLightStrength() + + m_cachedTheme->lightStrength() / 7.0f); m_barGradientShader->setUniformValue(m_barGradientShader->gradientMin(), 0.0f); m_barGradientShader->setUniformValue(m_barGradientShader->lightColor(), lightColor); diff --git a/src/datavisualization/engine/surface3drenderer.cpp b/src/datavisualization/engine/surface3drenderer.cpp index 96574807..96034039 100644 --- a/src/datavisualization/engine/surface3drenderer.cpp +++ b/src/datavisualization/engine/surface3drenderer.cpp @@ -277,7 +277,7 @@ void Surface3DRenderer::updateSurfaceTextures(QVector series if (!series->texture().isNull()) { cache->setSurfaceTexture(m_textureHelper->create2DTexture( - series->texture(), true, true, true)); + series->texture(), true, true, true)); if (cache->isFlatShadingEnabled()) cache->surfaceObject()->coarseUVs(array, cache->dataArray()); @@ -780,6 +780,19 @@ void Surface3DRenderer::drawSlicedScene() AxisRenderCache &sliceCache = rowMode ? m_axisCacheX : m_axisCacheZ; GLfloat scaleXBackground = 0.0f; + if (rowMode) { + // Don't use the regular margin for polar, as the graph is not going to be to scale anyway, + // and polar graphs often have quite a bit of margin, resulting in ugly slices. + if (m_polarGraph) + scaleXBackground = m_scaleX + 0.1f; + else + scaleXBackground = m_scaleXWithBackground; + } else { + if (m_polarGraph) + scaleXBackground = m_scaleZ + 0.1f; + else + scaleXBackground = m_scaleZWithBackground; + } // Disable culling to avoid ugly conditionals with reversed axes and data glDisable(GL_CULL_FACE); @@ -796,11 +809,6 @@ void Surface3DRenderer::drawSlicedScene() drawGrid = true; } - if (rowMode) - scaleXBackground = m_scaleXWithBackground; - else - scaleXBackground = m_scaleZWithBackground; - QMatrix4x4 MVPMatrix; QMatrix4x4 modelMatrix; QMatrix4x4 itModelMatrix; @@ -819,9 +827,27 @@ void Surface3DRenderer::drawSlicedScene() surfaceShader->bind(); - GLuint colorTexture = cache->baseUniformTexture();; - if (cache->colorStyle() != Q3DTheme::ColorStyleUniform) + GLuint colorTexture = cache->baseUniformTexture(); + if (cache->colorStyle() == Q3DTheme::ColorStyleUniform) { + colorTexture = cache->baseUniformTexture(); + surfaceShader->setUniformValue(surfaceShader->gradientMin(), 0.0f); + surfaceShader->setUniformValue(surfaceShader->gradientHeight(), 0.0f); + } else { colorTexture = cache->baseGradientTexture(); + if (cache->colorStyle() == Q3DTheme::ColorStyleObjectGradient) { + float objMin = cache->surfaceObject()->minYValue(); + float objMax = cache->surfaceObject()->maxYValue(); + float objRange = objMax - objMin; + surfaceShader->setUniformValue(surfaceShader->gradientMin(), + -(objMin / objRange)); + surfaceShader->setUniformValue(surfaceShader->gradientHeight(), + 1.0f / objRange); + } else { + surfaceShader->setUniformValue(surfaceShader->gradientMin(), 0.5f); + surfaceShader->setUniformValue(surfaceShader->gradientHeight(), + 1.0f / (m_scaleY * 2.0f)); + } + } // Set shader bindings surfaceShader->setUniformValue(surfaceShader->lightP(), lightPos); @@ -830,9 +856,10 @@ void Surface3DRenderer::drawSlicedScene() surfaceShader->setUniformValue(surfaceShader->nModel(), itModelMatrix.inverted().transposed()); surfaceShader->setUniformValue(surfaceShader->MVP(), MVPMatrix); - surfaceShader->setUniformValue(surfaceShader->lightS(), 0.15f); + surfaceShader->setUniformValue(surfaceShader->lightS(), 0.0f); surfaceShader->setUniformValue(surfaceShader->ambientS(), - m_cachedTheme->ambientLightStrength() * 2.3f); + m_cachedTheme->ambientLightStrength() + + m_cachedTheme->lightStrength() / 10.0f); surfaceShader->setUniformValue(surfaceShader->lightColor(), lightColor); m_drawer->drawObject(surfaceShader, cache->sliceSurfaceObject(), colorTexture); @@ -879,7 +906,8 @@ void Surface3DRenderer::drawSlicedScene() lineShader->setUniformValue(lineShader->view(), viewMatrix); lineShader->setUniformValue(lineShader->color(), lineColor); lineShader->setUniformValue(lineShader->ambientS(), - m_cachedTheme->ambientLightStrength() * 2.3f); + m_cachedTheme->ambientLightStrength() + + m_cachedTheme->lightStrength() / 10.0f); lineShader->setUniformValue(lineShader->lightS(), 0.0f); lineShader->setUniformValue(lineShader->lightColor(), lightColor); @@ -989,7 +1017,10 @@ void Surface3DRenderer::drawSlicedScene() for (int label = 0; label < labelCount; label++) { if (countLabelItems > labelNbr) { // Draw the label here - labelTrans.setX(sliceCache.labelPosition(label)); + if (rowMode) + labelTrans.setX(sliceCache.labelPosition(label)); + else + labelTrans.setX(-sliceCache.labelPosition(label)); m_dummyRenderItem.setTranslation(labelTrans); -- cgit v1.2.3 From 871e4bcab91b5eba810ee24e1ef00cc5c93d1a00 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Fri, 4 Jul 2014 15:01:27 +0300 Subject: Fix polar background margin not reacting to titles correctly Hiding angular axis title didn't cause the background margin to reduce appropriately. Change-Id: Icf62aaf62b978524dc4ac13c1f8561b517103c55 Reviewed-by: Mika Salmela --- src/datavisualization/engine/surface3drenderer.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/datavisualization/engine/surface3drenderer.cpp b/src/datavisualization/engine/surface3drenderer.cpp index 96034039..b718cb14 100644 --- a/src/datavisualization/engine/surface3drenderer.cpp +++ b/src/datavisualization/engine/surface3drenderer.cpp @@ -2477,13 +2477,12 @@ void Surface3DRenderer::idToRGBA(uint id, uchar *r, uchar *g, uchar *b, uchar *a void Surface3DRenderer::calculateSceneScalingFactors() { + // Margin for background (0.10 make it 10% larger to avoid + // selection ball being drawn inside background) + m_hBackgroundMargin = 0.1f; if (m_polarGraph) { float polarMargin = calculatePolarBackgroundMargin(); m_hBackgroundMargin = qMax(m_hBackgroundMargin, polarMargin); - } else { - // Margin for background (0.10 make it 10% larger to avoid - // selection ball being drawn inside background) - m_hBackgroundMargin = 0.1f; } // Calculate scene scaling and translation factors -- cgit v1.2.3 From d5ccfe2c0aa2ab1d14619e41561b5a6603296c8d Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Fri, 4 Jul 2014 15:08:22 +0300 Subject: Added mention of WinRT only supporting Quick2 api Change-Id: Ia92494674bfc618d9368075dabe6ea0490e6c3d1 Reviewed-by: Mika Salmela --- README | 4 ++-- src/datavisualization/doc/src/qtdatavisualization.qdoc | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README b/README index 911edbb2..b06437ce 100644 --- a/README +++ b/README @@ -74,8 +74,8 @@ Please refer to the generated documentation for more information: Known Issues ============ -- Android doesn't support both widgets and OpenGL simultaneously, so only - the Qt Quick 2 API is usable in practice in Android. +- Some platforms like Android and WinRT cannot handle multiple native windows properly, + so only the Qt Quick 2 versions of graphs are available in practice for those platforms. - Shadows are not supported with OpenGL ES2 (including Angle builds in Windows). - Anti-aliasing doesn't work with OpenGL ES2 (including Angle builds in Windows). - Surfaces with non-straight rows and columns do not always render properly. diff --git a/src/datavisualization/doc/src/qtdatavisualization.qdoc b/src/datavisualization/doc/src/qtdatavisualization.qdoc index 3726e735..a4718887 100644 --- a/src/datavisualization/doc/src/qtdatavisualization.qdoc +++ b/src/datavisualization/doc/src/qtdatavisualization.qdoc @@ -323,8 +323,8 @@ \title Qt Data Visualization Known Issues \list - \li Android doesn't support both widgets and OpenGL simultaneously, so only - the Qt Quick 2 API is usable in practice in Android. + \li Some platforms like Android and WinRT cannot handle multiple native windows properly, + so only the Qt Quick 2 graphs are available in practice for those platforms. \li Shadows are not supported with OpenGL ES2 (including Angle builds in Windows). \li Anti-aliasing doesn't work with OpenGL ES2 (including Angle builds in Windows). \li Surfaces with non-straight rows and columns do not always render properly. -- cgit v1.2.3 From 4ca6b07cf1a48b9d545c2a1aa7bc6801f93e07e6 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Thu, 31 Jul 2014 15:29:55 +0200 Subject: Avoid the Missing cmake tests error from qmake with Qt 5.4 Due to some bizarre new policy, any Qt module will fail unless tests/auto/cmake is present. See qtbase/mkspecs/features/create_cmake.prf. We don't care about cmake so just disable this thing in the .qmake.conf. Change-Id: I27b5b2e498d6056307b7e881ebc2b8e83978e5de Reviewed-by: Miikka Heikkinen --- .qmake.conf | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.qmake.conf b/.qmake.conf index 9c6617fa..4ac38ae2 100644 --- a/.qmake.conf +++ b/.qmake.conf @@ -2,3 +2,5 @@ load(qt_build_config) CONFIG += qt_example_installs MODULE_VERSION=1.1.0 + +CMAKE_MODULE_TESTS=- -- cgit v1.2.3 From 73f127d8ef1937aa77ba0ec0be63f0bfd6cf92ab Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Mon, 4 Aug 2014 12:05:38 +0300 Subject: Reduce the size of surface selection texture MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now four times more vertices are supported. Task-number: QTRD-3244 Change-Id: I80594f4c4f80f485d94dbc2f73892251bb20c521 Reviewed-by: Tomi Korpipää Reviewed-by: Titta Heikkala Reviewed-by: Mika Salmela --- src/datavisualization/engine/surface3drenderer.cpp | 35 ++++++++-------------- src/datavisualization/engine/surface3drenderer_p.h | 2 +- 2 files changed, 13 insertions(+), 24 deletions(-) diff --git a/src/datavisualization/engine/surface3drenderer.cpp b/src/datavisualization/engine/surface3drenderer.cpp index b718cb14..83b5c7fb 100644 --- a/src/datavisualization/engine/surface3drenderer.cpp +++ b/src/datavisualization/engine/surface3drenderer.cpp @@ -2389,11 +2389,12 @@ void Surface3DRenderer::updateSelectionTextures() void Surface3DRenderer::createSelectionTexture(SurfaceSeriesRenderCache *cache, uint &lastSelectionId) { - // Create the selection ID image. Each grid corner gets 2x2 pixel area of - // ID color so that each vertex (data point) has 4x4 pixel area of ID color + // Create the selection ID image. Each grid corner gets 1 pixel area of + // ID color so that each vertex (data point) has 2x2 pixel area of ID color, + // except the vertices on the edges. const QRect &sampleSpace = cache->sampleSpace(); - int idImageWidth = (sampleSpace.width() - 1) * 4; - int idImageHeight = (sampleSpace.height() - 1) * 4; + int idImageWidth = (sampleSpace.width() - 1) * 2; + int idImageHeight = (sampleSpace.height() - 1) * 2; if (idImageHeight <= 0 || idImageWidth <= 0) { cache->setSelectionIdRange(-1, -1); @@ -2405,21 +2406,21 @@ void Surface3DRenderer::createSelectionTexture(SurfaceSeriesRenderCache *cache, uint idStart = lastSelectionId; uchar *bits = new uchar[idImageWidth * idImageHeight * 4 * sizeof(uchar)]; - for (int i = 0; i < idImageHeight; i += 4) { - for (int j = 0; j < idImageWidth; j += 4) { + for (int i = 0; i < idImageHeight; i += 2) { + for (int j = 0; j < idImageWidth; j += 2) { int p = (i * idImageWidth + j) * 4; uchar r, g, b, a; idToRGBA(lastSelectionId, &r, &g, &b, &a); - fillIdCorner(&bits[p], r, g, b, a, stride); + fillIdCorner(&bits[p], r, g, b, a); idToRGBA(lastSelectionId + 1, &r, &g, &b, &a); - fillIdCorner(&bits[p + 8], r, g, b, a, stride); + fillIdCorner(&bits[p + 4], r, g, b, a); idToRGBA(lastSelectionId + sampleSpace.width(), &r, &g, &b, &a); - fillIdCorner(&bits[p + 2 * stride], r, g, b, a, stride); + fillIdCorner(&bits[p + stride], r, g, b, a); idToRGBA(lastSelectionId + sampleSpace.width() + 1, &r, &g, &b, &a); - fillIdCorner(&bits[p + 2 * stride + 8], r, g, b, a, stride); + fillIdCorner(&bits[p + stride + 4], r, g, b, a); lastSelectionId++; } @@ -2447,24 +2448,12 @@ void Surface3DRenderer::initSelectionBuffer() m_selectionDepthBuffer); } -void Surface3DRenderer::fillIdCorner(uchar *p, uchar r, uchar g, uchar b, uchar a, int stride) +void Surface3DRenderer::fillIdCorner(uchar *p, uchar r, uchar g, uchar b, uchar a) { p[0] = r; p[1] = g; p[2] = b; p[3] = a; - p[4] = r; - p[5] = g; - p[6] = b; - p[7] = a; - p[stride + 0] = r; - p[stride + 1] = g; - p[stride + 2] = b; - p[stride + 3] = a; - p[stride + 4] = r; - p[stride + 5] = g; - p[stride + 6] = b; - p[stride + 7] = a; } void Surface3DRenderer::idToRGBA(uint id, uchar *r, uchar *g, uchar *b, uchar *a) diff --git a/src/datavisualization/engine/surface3drenderer_p.h b/src/datavisualization/engine/surface3drenderer_p.h index c276c1f9..f34a927a 100644 --- a/src/datavisualization/engine/surface3drenderer_p.h +++ b/src/datavisualization/engine/surface3drenderer_p.h @@ -144,7 +144,7 @@ private: void updateSelectionTextures(); void createSelectionTexture(SurfaceSeriesRenderCache *cache, uint &lastSelectionId); void idToRGBA(uint id, uchar *r, uchar *g, uchar *b, uchar *a); - void fillIdCorner(uchar *p, uchar r, uchar g, uchar b, uchar a, int stride); + void fillIdCorner(uchar *p, uchar r, uchar g, uchar b, uchar a); void surfacePointSelected(const QPoint &point); void updateSelectionPoint(SurfaceSeriesRenderCache *cache, const QPoint &point, bool label); QPoint selectionIdToSurfacePoint(uint id); -- cgit v1.2.3 From dd99eb73740ad015a2a2d28481e5a2ca8ab1d7b1 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Tue, 5 Aug 2014 14:00:08 +0300 Subject: Enable camera targeting. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also fix custom item positioning in absolute mode as it was completely broken in bars and z-coord was flipped in others. Clarified Q3DObject::position property usage, namely that it is reserved for internal use for now. Some refactoring also done. Task-number: QTRD-2567 Change-Id: I5da65b83a2f8ecf20f8fd054e59748278ef1a714 Reviewed-by: Titta Heikkala Reviewed-by: Tomi Korpipää --- .../engine/abstract3drenderer.cpp | 35 ++++--- .../engine/abstract3drenderer_p.h | 4 + src/datavisualization/engine/bars3dcontroller.cpp | 10 +- src/datavisualization/engine/bars3drenderer.cpp | 93 ++++++++--------- src/datavisualization/engine/bars3drenderer_p.h | 16 ++- src/datavisualization/engine/q3dcamera.cpp | 81 +++++++++++---- src/datavisualization/engine/q3dcamera.h | 5 + src/datavisualization/engine/q3dcamera_p.h | 3 +- src/datavisualization/engine/q3dlight.cpp | 9 +- src/datavisualization/engine/q3dobject.cpp | 9 +- src/datavisualization/engine/q3dscene.cpp | 6 ++ src/datavisualization/engine/q3dscene_p.h | 2 + src/datavisualization/engine/scatter3drenderer.cpp | 20 ++-- src/datavisualization/engine/scatter3drenderer_p.h | 2 +- src/datavisualization/engine/surface3drenderer.cpp | 24 ++--- src/datavisualization/engine/surface3drenderer_p.h | 2 +- .../datavisualizationqml2_plugin.cpp | 1 + tests/barstest/chart.cpp | 24 +++++ tests/barstest/chart.h | 4 + tests/barstest/main.cpp | 28 ++++- tests/qmlcamera/qml/qmlcamera/main.qml | 27 +++-- tests/scattertest/main.cpp | 47 +++++++-- tests/scattertest/scatterchart.cpp | 24 +++++ tests/scattertest/scatterchart.h | 5 + tests/surfacetest/graphmodifier.cpp | 24 +++++ tests/surfacetest/graphmodifier.h | 4 + tests/surfacetest/main.cpp | 115 +++++++++++++-------- 27 files changed, 434 insertions(+), 190 deletions(-) diff --git a/src/datavisualization/engine/abstract3drenderer.cpp b/src/datavisualization/engine/abstract3drenderer.cpp index c2ed43da..14cf7109 100644 --- a/src/datavisualization/engine/abstract3drenderer.cpp +++ b/src/datavisualization/engine/abstract3drenderer.cpp @@ -79,7 +79,8 @@ Abstract3DRenderer::Abstract3DRenderer(Abstract3DController *controller) m_xFlipRotation(QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, -180.0f)), m_zFlipRotation(QQuaternion::fromAxisAndAngle(0.0f, 0.0f, 1.0f, -180.0f)), m_vBackgroundMargin(0.1f), - m_hBackgroundMargin(0.1f) + m_hBackgroundMargin(0.1f), + m_oldCameraTarget(QVector3D(2000.0f, 2000.0f, 2000.0f)) // Just random invalid target { QObject::connect(m_drawer, &Drawer::drawerChanged, this, &Abstract3DRenderer::updateTextures); QObject::connect(this, &Abstract3DRenderer::needRender, controller, @@ -220,10 +221,6 @@ void Abstract3DRenderer::updateScene(Q3DScene *scene) handleResize(); } - scene->activeCamera()->d_ptr->updateViewMatrix(m_autoScaleAdjustment); - // Set light position (rotate light with activeCamera, a bit above it (as set in defaultLightPos)) - scene->d_ptr->setLightPositionRelativeToCamera(defaultLightPos); - QPoint logicalPixelPosition = scene->selectionQueryPosition(); updateInputPosition(QPoint(logicalPixelPosition.x() * m_devicePixelRatio, logicalPixelPosition.y() * m_devicePixelRatio)); @@ -231,6 +228,8 @@ void Abstract3DRenderer::updateScene(Q3DScene *scene) // Synchronize the renderer scene to controller scene scene->d_ptr->sync(*m_cachedScene->d_ptr); + updateCameraViewport(); + if (Q3DScene::invalidSelectionPoint() == logicalPixelPosition) { updateSelectionState(SelectNone); } else { @@ -316,7 +315,6 @@ void Abstract3DRenderer::updateAspectRatio(float ratio) m_graphAspectRatio = ratio; foreach (SeriesRenderCache *cache, m_renderCacheList) cache->setDataDirty(true); - updateCustomItemPositions(); } void Abstract3DRenderer::updateHorizontalAspectRatio(float ratio) @@ -324,7 +322,6 @@ void Abstract3DRenderer::updateHorizontalAspectRatio(float ratio) m_graphHorizontalAspectRatio = ratio; foreach (SeriesRenderCache *cache, m_renderCacheList) cache->setDataDirty(true); - updateCustomItemPositions(); } void Abstract3DRenderer::updatePolar(bool enable) @@ -332,7 +329,6 @@ void Abstract3DRenderer::updatePolar(bool enable) m_polarGraph = enable; foreach (SeriesRenderCache *cache, m_renderCacheList) cache->setDataDirty(true); - updateCustomItemPositions(); } void Abstract3DRenderer::updateRadialLabelOffset(float offset) @@ -401,8 +397,6 @@ void Abstract3DRenderer::updateAxisRange(QAbstract3DAxis::AxisOrientation orient foreach (SeriesRenderCache *cache, m_renderCacheList) cache->setDataDirty(true); - - updateCustomItemPositions(); } void Abstract3DRenderer::updateAxisSegmentCount(QAbstract3DAxis::AxisOrientation orientation, @@ -431,8 +425,6 @@ void Abstract3DRenderer::updateAxisReversed(QAbstract3DAxis::AxisOrientation ori axisCacheForOrientation(orientation).setReversed(enable); foreach (SeriesRenderCache *cache, m_renderCacheList) cache->setDataDirty(true); - - updateCustomItemPositions(); } void Abstract3DRenderer::updateAxisFormatter(QAbstract3DAxis::AxisOrientation orientation, @@ -449,8 +441,6 @@ void Abstract3DRenderer::updateAxisFormatter(QAbstract3DAxis::AxisOrientation or foreach (SeriesRenderCache *cache, m_renderCacheList) cache->setDataDirty(true); - - updateCustomItemPositions(); } void Abstract3DRenderer::updateAxisLabelAutoRotation(QAbstract3DAxis::AxisOrientation orientation, @@ -1347,4 +1337,21 @@ float Abstract3DRenderer::calculatePolarBackgroundMargin() return maxNeededMargin; } +void Abstract3DRenderer::updateCameraViewport() +{ + QVector3D adjustedTarget = m_cachedScene->activeCamera()->target(); + fixCameraTarget(adjustedTarget); + if (m_oldCameraTarget != adjustedTarget) { + QVector3D cameraBase = cameraDistanceVector + adjustedTarget; + + m_cachedScene->activeCamera()->d_ptr->setBaseOrientation(cameraBase, + adjustedTarget, + upVector); + m_oldCameraTarget = adjustedTarget; + } + m_cachedScene->activeCamera()->d_ptr->updateViewMatrix(m_autoScaleAdjustment); + // Set light position (i.e rotate light with activeCamera, a bit above it) + m_cachedScene->d_ptr->setLightPositionRelativeToCamera(defaultLightPos); +} + QT_END_NAMESPACE_DATAVISUALIZATION diff --git a/src/datavisualization/engine/abstract3drenderer_p.h b/src/datavisualization/engine/abstract3drenderer_p.h index 40c7db0a..50370954 100644 --- a/src/datavisualization/engine/abstract3drenderer_p.h +++ b/src/datavisualization/engine/abstract3drenderer_p.h @@ -207,6 +207,8 @@ protected: const QMatrix4x4 &projectionViewMatrix, const QMatrix4x4 &depthMatrix); float calculatePolarBackgroundMargin(); + virtual void fixCameraTarget(QVector3D &target) = 0; + void updateCameraViewport(); bool m_hasNegativeValues; Q3DTheme *m_cachedTheme; @@ -274,6 +276,8 @@ protected: float m_vBackgroundMargin; float m_hBackgroundMargin; + QVector3D m_oldCameraTarget; + private: friend class Abstract3DController; }; diff --git a/src/datavisualization/engine/bars3dcontroller.cpp b/src/datavisualization/engine/bars3dcontroller.cpp index 3a240c24..bbec6967 100644 --- a/src/datavisualization/engine/bars3dcontroller.cpp +++ b/src/datavisualization/engine/bars3dcontroller.cpp @@ -114,12 +114,10 @@ void Bars3DController::synchDataToRenderer() m_changeTracker.selectedBarChanged = false; } - if (needSceneUpdate) { - // Since scene is updated before axis updates are handled, - // do another render pass for scene update - m_scene->d_ptr->m_sceneDirty = true; - emitNeedRender(); - } + // Since scene is updated before axis updates are handled, do another render pass to + // properly update controller side camera limits. + if (needSceneUpdate) + m_scene->d_ptr->markDirty(); } void Bars3DController::handleArrayReset() diff --git a/src/datavisualization/engine/bars3drenderer.cpp b/src/datavisualization/engine/bars3drenderer.cpp index cd6c1eb0..38fa3147 100644 --- a/src/datavisualization/engine/bars3drenderer.cpp +++ b/src/datavisualization/engine/bars3drenderer.cpp @@ -65,7 +65,6 @@ Bars3DRenderer::Bars3DRenderer(Bars3DController *controller) m_scaleFactor(0), m_maxSceneSize(40.0f), m_visualSelectedBarPos(Bars3DController::invalidSelectionPosition()), - m_resetCameraBaseOrientation(true), m_selectedBarPos(Bars3DController::invalidSelectionPosition()), m_selectedSeriesCache(0), m_noZeroInRange(false), @@ -77,7 +76,9 @@ Bars3DRenderer::Bars3DRenderer(Bars3DController *controller) m_keepSeriesUniform(false), m_haveUniformColorSeries(false), m_haveGradientSeries(false), - m_zeroPosition(0.0f) + m_zeroPosition(0.0f), + m_xScaleFactor(1.0f), + m_zScaleFactor(1.0f) { m_axisCacheY.setScale(2.0f); m_axisCacheY.setTranslate(-1.0f); @@ -130,6 +131,13 @@ void Bars3DRenderer::initializeOpenGL() loadBackgroundMesh(); } +void Bars3DRenderer::fixCameraTarget(QVector3D &target) +{ + target.setX(target.x() * m_xScaleFactor); + target.setY(0.0f); + target.setZ(target.z() * -m_zScaleFactor); +} + void Bars3DRenderer::updateData() { int minRow = m_axisCacheZ.min(); @@ -161,10 +169,10 @@ void Bars3DRenderer::updateData() GLfloat sceneRatio = qMin(GLfloat(newColumns) / GLfloat(newRows), GLfloat(newRows) / GLfloat(newColumns)); m_maxSceneSize = 2.0f * qSqrt(sceneRatio * newColumns * newRows); - // Calculate here and at setting bar specs - calculateSceneScalingFactors(); } + calculateSceneScalingFactors(); + m_zeroPosition = m_axisCacheY.formatter()->positionAt(0.0f); foreach (SeriesRenderCache *baseCache, m_renderCacheList) { @@ -388,14 +396,6 @@ void Bars3DRenderer::updateScene(Q3DScene *scene) } } - if (m_resetCameraBaseOrientation) { - // Set initial camera position. Also update if height adjustment has changed. - scene->activeCamera()->d_ptr->setBaseOrientation(cameraDistanceVector, - zeroVector, - upVector); - m_resetCameraBaseOrientation = false; - } - Abstract3DRenderer::updateScene(scene); updateSlicingActive(scene->isSlicingActive()); @@ -986,9 +986,6 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) QMatrix4x4 projectionViewMatrix = projectionMatrix * viewMatrix; - GLfloat rowScaleFactor = m_rowWidth / m_scaleFactor; - GLfloat columnScaleFactor = m_columnDepth / m_scaleFactor; - BarRenderItem *selectedBar(0); #if !defined(QT_OPENGL_ES_2) @@ -1185,8 +1182,7 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) Abstract3DRenderer::drawCustomItems(RenderingSelection, m_selectionShader, viewMatrix, projectionViewMatrix, depthProjectionViewMatrix, m_depthTexture, m_shadowQualityToShader); - drawLabels(true, activeCamera, viewMatrix, projectionMatrix, rowScaleFactor, - columnScaleFactor); + drawLabels(true, activeCamera, viewMatrix, projectionMatrix); glEnable(GL_DITHER); // Read color under cursor @@ -1216,8 +1212,7 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) glStencilFunc(GL_ALWAYS, 1, 0xffffffff); // Draw background stencil - drawBackground(rowScaleFactor, columnScaleFactor, backgroundRotation, - depthProjectionViewMatrix, projectionViewMatrix, viewMatrix); + drawBackground(backgroundRotation, depthProjectionViewMatrix, projectionViewMatrix, viewMatrix); glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); glEnable(GL_DEPTH_TEST); @@ -1255,12 +1250,11 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) #ifdef USE_REFLECTIONS glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - drawBackground(rowScaleFactor, columnScaleFactor, backgroundRotation, - depthProjectionViewMatrix, projectionViewMatrix, viewMatrix, 0.5f); + drawBackground(backgroundRotation, depthProjectionViewMatrix, projectionViewMatrix, viewMatrix, + 0.5f); glDisable(GL_BLEND); #else - drawBackground(rowScaleFactor, columnScaleFactor, backgroundRotation, - depthProjectionViewMatrix, projectionViewMatrix, viewMatrix); + drawBackground(backgroundRotation, depthProjectionViewMatrix, projectionViewMatrix, viewMatrix); #endif // Draw bars @@ -1270,8 +1264,7 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) startBar, stopBar, stepBar); // Draw grid lines - drawGridLines(rowScaleFactor, columnScaleFactor, depthProjectionViewMatrix, - projectionViewMatrix, viewMatrix); + drawGridLines(depthProjectionViewMatrix, projectionViewMatrix, viewMatrix); // Draw custom items Abstract3DRenderer::drawCustomItems(RenderingNormal, m_customItemShader, viewMatrix, @@ -1279,8 +1272,7 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) m_depthTexture, m_shadowQualityToShader); // Draw labels - drawLabels(false, activeCamera, viewMatrix, projectionMatrix, rowScaleFactor, - columnScaleFactor); + drawLabels(false, activeCamera, viewMatrix, projectionMatrix); // Handle selected bar label generation if (barSelectionFound) { @@ -1657,14 +1649,12 @@ bool Bars3DRenderer::drawBars(BarRenderItem **selectedBar, } #ifdef USE_REFLECTIONS -void Bars3DRenderer::drawBackground(GLfloat rowScaleFactor, GLfloat columnScaleFactor, - GLfloat backgroundRotation, +void Bars3DRenderer::drawBackground(GLfloat backgroundRotation, const QMatrix4x4 &depthProjectionViewMatrix, const QMatrix4x4 &projectionViewMatrix, const QMatrix4x4 &viewMatrix, GLfloat reflection) #else -void Bars3DRenderer::drawBackground(GLfloat rowScaleFactor, GLfloat columnScaleFactor, - GLfloat backgroundRotation, +void Bars3DRenderer::drawBackground(GLfloat backgroundRotation, const QMatrix4x4 &depthProjectionViewMatrix, const QMatrix4x4 &projectionViewMatrix, const QMatrix4x4 &viewMatrix) @@ -1683,7 +1673,7 @@ void Bars3DRenderer::drawBackground(GLfloat rowScaleFactor, GLfloat columnScaleF QMatrix4x4 MVPMatrix; QMatrix4x4 itModelMatrix; - QVector3D backgroundScaler(rowScaleFactor, 1.0f, columnScaleFactor); + QVector3D backgroundScaler(m_xScaleFactor, 1.0f, m_zScaleFactor); QVector4D backgroundColor = Utils::vectorFromColor(m_cachedTheme->backgroundColor()); #ifdef USE_REFLECTIONS backgroundColor.setW(backgroundColor.w() * reflection); @@ -1787,8 +1777,7 @@ void Bars3DRenderer::drawBackground(GLfloat rowScaleFactor, GLfloat columnScaleF } } -void Bars3DRenderer::drawGridLines(GLfloat rowScaleFactor, GLfloat columnScaleFactor, - const QMatrix4x4 &depthProjectionViewMatrix, +void Bars3DRenderer::drawGridLines(const QMatrix4x4 &depthProjectionViewMatrix, const QMatrix4x4 &projectionViewMatrix, const QMatrix4x4 &viewMatrix) { @@ -1832,7 +1821,7 @@ void Bars3DRenderer::drawGridLines(GLfloat rowScaleFactor, GLfloat columnScaleFa if (m_yFlipped) yFloorLinePosition = -yFloorLinePosition; - QVector3D gridLineScaler(rowScaleFactor, gridLineWidth, gridLineWidth); + QVector3D gridLineScaler(m_xScaleFactor, gridLineWidth, gridLineWidth); if (m_yFlipped) lineRotation = m_xRightAngleRotation; @@ -1881,7 +1870,7 @@ void Bars3DRenderer::drawGridLines(GLfloat rowScaleFactor, GLfloat columnScaleFa #if defined(QT_OPENGL_ES_2) lineRotation = m_yRightAngleRotation; #endif - gridLineScaler = QVector3D(gridLineWidth, gridLineWidth, columnScaleFactor); + gridLineScaler = QVector3D(gridLineWidth, gridLineWidth, m_zScaleFactor); for (GLfloat bar = 0.0f; bar <= m_cachedColumnCount; bar++) { QMatrix4x4 modelMatrix; QMatrix4x4 MVPMatrix; @@ -1923,11 +1912,11 @@ void Bars3DRenderer::drawGridLines(GLfloat rowScaleFactor, GLfloat columnScaleFa // Wall lines: back wall int gridLineCount = m_axisCacheY.gridLineCount(); - GLfloat zWallLinePosition = -columnScaleFactor + gridLineOffset; + GLfloat zWallLinePosition = -m_zScaleFactor + gridLineOffset; if (m_zFlipped) zWallLinePosition = -zWallLinePosition; - gridLineScaler = QVector3D(rowScaleFactor, gridLineWidth, gridLineWidth); + gridLineScaler = QVector3D(m_xScaleFactor, gridLineWidth, gridLineWidth); for (int line = 0; line < gridLineCount; line++) { QMatrix4x4 modelMatrix; QMatrix4x4 MVPMatrix; @@ -1968,7 +1957,7 @@ void Bars3DRenderer::drawGridLines(GLfloat rowScaleFactor, GLfloat columnScaleFa } // Wall lines: side wall - GLfloat xWallLinePosition = -rowScaleFactor + gridLineOffset; + GLfloat xWallLinePosition = -m_xScaleFactor + gridLineOffset; if (m_xFlipped) xWallLinePosition = -xWallLinePosition; @@ -1977,7 +1966,7 @@ void Bars3DRenderer::drawGridLines(GLfloat rowScaleFactor, GLfloat columnScaleFa else lineRotation = m_yRightAngleRotation; - gridLineScaler = QVector3D(gridLineWidth, gridLineWidth, columnScaleFactor); + gridLineScaler = QVector3D(gridLineWidth, gridLineWidth, m_zScaleFactor); for (int line = 0; line < gridLineCount; line++) { QMatrix4x4 modelMatrix; QMatrix4x4 MVPMatrix; @@ -2019,8 +2008,7 @@ void Bars3DRenderer::drawGridLines(GLfloat rowScaleFactor, GLfloat columnScaleFa } void Bars3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCamera, - const QMatrix4x4 &viewMatrix, const QMatrix4x4 &projectionMatrix, - GLfloat rowScaleFactor, GLfloat columnScaleFactor) { + const QMatrix4x4 &viewMatrix, const QMatrix4x4 &projectionMatrix) { ShaderHelper *shader = 0; GLfloat alphaForValueSelection = labelValueAlpha / 255.0f; GLfloat alphaForRowSelection = labelRowAlpha / 255.0f; @@ -2052,8 +2040,8 @@ void Bars3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCamer int labelCount = m_axisCacheY.labelCount(); GLfloat labelMarginXTrans = labelMargin; GLfloat labelMarginZTrans = labelMargin; - GLfloat labelXTrans = rowScaleFactor; - GLfloat labelZTrans = columnScaleFactor; + GLfloat labelXTrans = m_xScaleFactor; + GLfloat labelZTrans = m_zScaleFactor; QVector3D backLabelRotation(0.0f, -90.0f, 0.0f); QVector3D sideLabelRotation(0.0f, 0.0f, 0.0f); Qt::AlignmentFlag backAlignment = (m_xFlipped != m_zFlipped) ? Qt::AlignLeft : Qt::AlignRight; @@ -2157,8 +2145,8 @@ void Bars3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCamer fractionCamY = activeCamera->yRotation() * labelAngleFraction; fractionCamX = activeCamera->xRotation() * labelAngleFraction; GLfloat labelYAdjustment = 0.005f; - GLfloat scaledRowWidth = rowScaleFactor; - GLfloat scaledColumnDepth = columnScaleFactor; + GLfloat scaledRowWidth = m_xScaleFactor; + GLfloat scaledColumnDepth = m_zScaleFactor; GLfloat colPosValue = scaledRowWidth + labelMargin; GLfloat rowPosValue = scaledColumnDepth + labelMargin; GLfloat rowPos = 0.0f; @@ -2580,8 +2568,17 @@ void Bars3DRenderer::calculateSceneScalingFactors() m_maxDimension = qMax(m_rowWidth, m_columnDepth); m_scaleFactor = qMin((m_cachedColumnCount * (m_maxDimension / m_maxSceneSize)), (m_cachedRowCount * (m_maxDimension / m_maxSceneSize))); + + // Single bar scaling m_scaleX = m_cachedBarThickness.width() / m_scaleFactor; m_scaleZ = m_cachedBarThickness.height() / m_scaleFactor; + + // Whole graph scale factors + m_xScaleFactor = m_rowWidth / m_scaleFactor; + m_zScaleFactor = m_columnDepth / m_scaleFactor; + + updateCameraViewport(); + updateCustomItemPositions(); } void Bars3DRenderer::calculateHeightAdjustment() @@ -2815,9 +2812,9 @@ QVector3D Bars3DRenderer::convertPositionToTranslation(const QVector3D &position / m_scaleFactor; yTrans = m_axisCacheY.positionAt(position.y()); } else { - xTrans = position.x() * m_scaleX; + xTrans = position.x() * m_xScaleFactor; yTrans = position.y() + m_backgroundAdjustment; - zTrans = position.z() * m_scaleZ; + zTrans = position.z() * -m_zScaleFactor; } return QVector3D(xTrans, yTrans, zTrans); } diff --git a/src/datavisualization/engine/bars3drenderer_p.h b/src/datavisualization/engine/bars3drenderer_p.h index 466f7bed..cf29dc21 100644 --- a/src/datavisualization/engine/bars3drenderer_p.h +++ b/src/datavisualization/engine/bars3drenderer_p.h @@ -85,7 +85,6 @@ private: GLfloat m_scaleFactor; GLfloat m_maxSceneSize; QPoint m_visualSelectedBarPos; - bool m_resetCameraBaseOrientation; QPoint m_selectedBarPos; BarSeriesRenderCache *m_selectedSeriesCache; BarRenderItem m_dummyBarRenderItem; @@ -99,6 +98,8 @@ private: bool m_haveUniformColorSeries; bool m_haveGradientSeries; float m_zeroPosition; + float m_xScaleFactor; + float m_zScaleFactor; public: explicit Bars3DRenderer(Bars3DController *controller); @@ -118,6 +119,7 @@ public: protected: virtual void initializeOpenGL(); + virtual void fixCameraTarget(QVector3D &target); public slots: void updateMultiSeriesScaling(bool uniform); @@ -145,16 +147,14 @@ private: void drawSlicedScene(); void drawScene(GLuint defaultFboHandle); void drawLabels(bool drawSelection, const Q3DCamera *activeCamera, - const QMatrix4x4 &viewMatrix, const QMatrix4x4 &projectionMatrix, - GLfloat rowScaleFactor, GLfloat columnScaleFactor); + const QMatrix4x4 &viewMatrix, const QMatrix4x4 &projectionMatrix); #ifdef USE_REFLECTIONS bool drawBars(BarRenderItem **selectedBar, const QMatrix4x4 &depthProjectionViewMatrix, const QMatrix4x4 &projectionViewMatrix, const QMatrix4x4 &viewMatrix, GLint startRow, GLint stopRow, GLint stepRow, GLint startBar, GLint stopBar, GLint stepBar, GLfloat reflection = 1.0f); - void drawBackground(GLfloat rowScaleFactor, GLfloat columnScaleFactor, - GLfloat backgroundRotation, const QMatrix4x4 &depthProjectionViewMatrix, + void drawBackground(GLfloat backgroundRotation, const QMatrix4x4 &depthProjectionViewMatrix, const QMatrix4x4 &projectionViewMatrix, const QMatrix4x4 &viewMatrix, GLfloat reflection = 1.0f); #else @@ -162,12 +162,10 @@ private: const QMatrix4x4 &projectionViewMatrix, const QMatrix4x4 &viewMatrix, GLint startRow, GLint stopRow, GLint stepRow, GLint startBar, GLint stopBar, GLint stepBar); - void drawBackground(GLfloat rowScaleFactor, GLfloat columnScaleFactor, - GLfloat backgroundRotation, const QMatrix4x4 &depthProjectionViewMatrix, + void drawBackground(GLfloat backgroundRotation, const QMatrix4x4 &depthProjectionViewMatrix, const QMatrix4x4 &projectionViewMatrix, const QMatrix4x4 &viewMatrix); #endif - void drawGridLines(GLfloat rowScaleFactor, GLfloat columnScaleFactor, - const QMatrix4x4 &depthProjectionViewMatrix, + void drawGridLines(const QMatrix4x4 &depthProjectionViewMatrix, const QMatrix4x4 &projectionViewMatrix, const QMatrix4x4 &viewMatrix); diff --git a/src/datavisualization/engine/q3dcamera.cpp b/src/datavisualization/engine/q3dcamera.cpp index 06fd570c..0c2a8d82 100644 --- a/src/datavisualization/engine/q3dcamera.cpp +++ b/src/datavisualization/engine/q3dcamera.cpp @@ -136,6 +136,19 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION * and maximum values. */ +/*! + * \qmlproperty vector3d Camera3D::target + * \since QtDataVisualization 1.2 + * + * Holds the camera \a target as a vector3d. Defaults to \c {vector3d(0.0, 0.0, 0.0)}. + * + * Valid coordinate values are between \c{-1.0...1.0}, where the edge values indicate + * the edges of the corresponding axis range. Any values outside this range are clamped to the edge. + * + * \note For bar graphs, the Y-coordinate is ignored and camera always targets a point on + * the horizontal background. + */ + /*! * Constructs a new 3D camera with position set to origin, up direction facing towards the Y-axis * and looking at origin by default. An optional \a parent parameter can be given and is then passed @@ -160,22 +173,11 @@ Q3DCamera::~Q3DCamera() */ void Q3DCamera::copyValuesFrom(const Q3DObject &source) { - Q3DObject::copyValuesFrom(source); + // Note: Do not copy values from parent, as we are handling the position internally const Q3DCamera &sourceCamera = static_cast(source); - d_ptr->m_target.setX(sourceCamera.d_ptr->m_target.x()); - d_ptr->m_target.setY(sourceCamera.d_ptr->m_target.y()); - d_ptr->m_target.setZ(sourceCamera.d_ptr->m_target.z()); - - d_ptr->m_up.setX(sourceCamera.d_ptr->m_up.x()); - d_ptr->m_up.setY(sourceCamera.d_ptr->m_up.y()); - d_ptr->m_up.setZ(sourceCamera.d_ptr->m_up.z()); - - float *values = new float[16]; - sourceCamera.d_ptr->m_viewMatrix.copyDataTo(values); - d_ptr->m_viewMatrix = QMatrix4x4(values); - delete[] values; + d_ptr->m_requestedTarget = sourceCamera.d_ptr->m_requestedTarget; d_ptr->m_xRotation = sourceCamera.d_ptr->m_xRotation; d_ptr->m_yRotation = sourceCamera.d_ptr->m_yRotation; @@ -469,6 +471,49 @@ void Q3DCamera::setCameraPosition(float horizontal, float vertical, float zoom) setYRotation(vertical); } +/*! + * \property Q3DCamera::target + * \since QtDataVisualization 1.2 + * + * Holds the camera \a target as a QVector3D. Defaults to \c {QVector3D(0.0, 0.0, 0.0)}. + * + * Valid coordinate values are between \c{-1.0...1.0}, where the edge values indicate + * the edges of the corresponding axis range. Any values outside this range are clamped to the edge. + * + * \note For bar graphs, the Y-coordinate is ignored and camera always targets a point on + * the horizontal background. + */ +QVector3D Q3DCamera::target() const +{ + return d_ptr->m_requestedTarget; +} + +void Q3DCamera::setTarget(const QVector3D &target) +{ + QVector3D newTarget = target; + + if (newTarget.x() < -1.0f) + newTarget.setX(-1.0f); + else if (newTarget.x() > 1.0f) + newTarget.setX(1.0f); + + if (newTarget.y() < -1.0f) + newTarget.setY(-1.0f); + else if (newTarget.y() > 1.0f) + newTarget.setY(1.0f); + + if (newTarget.z() < -1.0f) + newTarget.setZ(-1.0f); + else if (newTarget.z() > 1.0f) + newTarget.setZ(1.0f); + + if (d_ptr->m_requestedTarget != newTarget) { + d_ptr->m_requestedTarget = newTarget; + setDirty(true); + emit targetChanged(newTarget); + } +} + Q3DCameraPrivate::Q3DCameraPrivate(Q3DCamera *q) : q_ptr(q), m_isViewMatrixUpdateActive(true), @@ -645,9 +690,9 @@ void Q3DCameraPrivate::updateViewMatrix(float zoomAdjustment) QMatrix4x4 viewMatrix; // Apply to view matrix - viewMatrix.lookAt(q_ptr->position(), m_target, m_up); + viewMatrix.lookAt(q_ptr->position(), m_actualTarget, m_up); // Compensate for translation (if d_ptr->m_target is off origin) - viewMatrix.translate(m_target.x(), m_target.y(), m_target.z()); + viewMatrix.translate(m_actualTarget.x(), m_actualTarget.y(), m_actualTarget.z()); // Apply rotations // Handle x and z rotation when y -angle is other than 0 viewMatrix.rotate(m_xRotation, 0, qCos(qDegreesToRadians(m_yRotation)), @@ -657,7 +702,7 @@ void Q3DCameraPrivate::updateViewMatrix(float zoomAdjustment) // handle zoom by scaling viewMatrix.scale(zoom / 100.0f); // Compensate for translation (if d_ptr->m_target is off origin) - viewMatrix.translate(-m_target.x(), -m_target.y(), -m_target.z()); + viewMatrix.translate(-m_actualTarget.x(), -m_actualTarget.y(), -m_actualTarget.z()); setViewMatrix(viewMatrix); } @@ -713,9 +758,9 @@ void Q3DCameraPrivate::setBaseOrientation(const QVector3D &basePosition, const QVector3D &target, const QVector3D &baseUp) { - if (q_ptr->position() != basePosition || m_target != target || m_up != baseUp) { + if (q_ptr->position() != basePosition || m_actualTarget != target || m_up != baseUp) { q_ptr->setPosition(basePosition); - m_target = target; + m_actualTarget = target; m_up = baseUp; q_ptr->setDirty(true); } diff --git a/src/datavisualization/engine/q3dcamera.h b/src/datavisualization/engine/q3dcamera.h index e9ad6dd2..a7da9031 100644 --- a/src/datavisualization/engine/q3dcamera.h +++ b/src/datavisualization/engine/q3dcamera.h @@ -35,6 +35,7 @@ class QT_DATAVISUALIZATION_EXPORT Q3DCamera : public Q3DObject Q_PROPERTY(CameraPreset cameraPreset READ cameraPreset WRITE setCameraPreset NOTIFY cameraPresetChanged) Q_PROPERTY(bool wrapXRotation READ wrapXRotation WRITE setWrapXRotation NOTIFY wrapXRotationChanged) Q_PROPERTY(bool wrapYRotation READ wrapYRotation WRITE setWrapYRotation NOTIFY wrapYRotationChanged) + Q_PROPERTY(QVector3D target READ target WRITE setTarget NOTIFY targetChanged REVISION 1) public: enum CameraPreset { @@ -89,6 +90,9 @@ public: void setCameraPosition(float horizontal, float vertical, float zoom = 100.0f); + QVector3D target() const; + void setTarget(const QVector3D &target); + signals: void xRotationChanged(float rotation); void yRotationChanged(float rotation); @@ -96,6 +100,7 @@ signals: void cameraPresetChanged(Q3DCamera::CameraPreset preset); void wrapXRotationChanged(bool isEnabled); void wrapYRotationChanged(bool isEnabled); + Q_REVISION(1) void targetChanged(const QVector3D &target); private: QScopedPointer d_ptr; diff --git a/src/datavisualization/engine/q3dcamera_p.h b/src/datavisualization/engine/q3dcamera_p.h index 01e7a508..6e9fd190 100644 --- a/src/datavisualization/engine/q3dcamera_p.h +++ b/src/datavisualization/engine/q3dcamera_p.h @@ -84,7 +84,7 @@ signals: public: Q3DCamera *q_ptr; - QVector3D m_target; + QVector3D m_actualTarget; QVector3D m_up; QMatrix4x4 m_viewMatrix; @@ -100,6 +100,7 @@ public: bool m_wrapXRotation; bool m_wrapYRotation; Q3DCamera::CameraPreset m_activePreset; + QVector3D m_requestedTarget; friend class Bars3DRenderer; friend class Surface3DRenderer; diff --git a/src/datavisualization/engine/q3dlight.cpp b/src/datavisualization/engine/q3dlight.cpp index 03f094cb..92b7bd07 100644 --- a/src/datavisualization/engine/q3dlight.cpp +++ b/src/datavisualization/engine/q3dlight.cpp @@ -68,13 +68,8 @@ Q3DLightPrivate::~Q3DLightPrivate() void Q3DLightPrivate::sync(Q3DLight &other) { - // Copies changed values from this light to the other light. If the other light had same changes, - // those changes are discarded. - if (q_ptr->isDirty()) { - other.copyValuesFrom(*q_ptr); - q_ptr->setDirty(false); - other.setDirty(false); - } + Q_UNUSED(other); + // Do nothing. Only value light has to sync is the position, which we handle internally. } QT_END_NAMESPACE_DATAVISUALIZATION diff --git a/src/datavisualization/engine/q3dobject.cpp b/src/datavisualization/engine/q3dobject.cpp index 56edb098..6b442ef8 100644 --- a/src/datavisualization/engine/q3dobject.cpp +++ b/src/datavisualization/engine/q3dobject.cpp @@ -53,9 +53,7 @@ Q3DObject::~Q3DObject() */ void Q3DObject::copyValuesFrom(const Q3DObject &source) { - d_ptr->m_position.setX(source.d_ptr->m_position.x()); - d_ptr->m_position.setY(source.d_ptr->m_position.y()); - d_ptr->m_position.setZ(source.d_ptr->m_position.z()); + d_ptr->m_position = source.d_ptr->m_position; setDirty(true); } @@ -74,6 +72,9 @@ Q3DScene *Q3DObject::parentScene() * \property Q3DObject::position * * This property contains the 3D position of the object. + * + * \note Currently setting this property has no effect, as the positions of Q3DObjects in the + * scene are handled internally. */ QVector3D Q3DObject::position() const { @@ -97,7 +98,7 @@ void Q3DObject::setDirty(bool dirty) { d_ptr->m_isDirty = dirty; if (parentScene()) - parentScene()->d_ptr->m_sceneDirty = true; + parentScene()->d_ptr->markDirty(); } /*! diff --git a/src/datavisualization/engine/q3dscene.cpp b/src/datavisualization/engine/q3dscene.cpp index 9464bc8d..3f2deec0 100644 --- a/src/datavisualization/engine/q3dscene.cpp +++ b/src/datavisualization/engine/q3dscene.cpp @@ -649,4 +649,10 @@ void Q3DScenePrivate::setLightPositionRelativeToCamera(const QVector3D &relative distanceModifier)); } +void Q3DScenePrivate::markDirty() +{ + m_sceneDirty = true; + emit needRender(); +} + QT_END_NAMESPACE_DATAVISUALIZATION diff --git a/src/datavisualization/engine/q3dscene_p.h b/src/datavisualization/engine/q3dscene_p.h index 2c69e5e0..c86a0ec1 100644 --- a/src/datavisualization/engine/q3dscene_p.h +++ b/src/datavisualization/engine/q3dscene_p.h @@ -89,6 +89,8 @@ public: float fixedRotation = 0.0f, float distanceModifier = 0.0f); + void markDirty(); + signals: void needRender(); diff --git a/src/datavisualization/engine/scatter3drenderer.cpp b/src/datavisualization/engine/scatter3drenderer.cpp index 5f8eede9..09225b99 100644 --- a/src/datavisualization/engine/scatter3drenderer.cpp +++ b/src/datavisualization/engine/scatter3drenderer.cpp @@ -67,7 +67,6 @@ Scatter3DRenderer::Scatter3DRenderer(Scatter3DController *controller) m_selectedSeriesCache(0), m_oldSelectedSeriesCache(0), m_dotSizeScale(1.0f), - m_hasHeightAdjustmentChanged(true), m_maxItemSize(0.0f), m_clickedIndex(Scatter3DController::invalidSelectionIndex()), m_havePointSeries(false), @@ -133,6 +132,13 @@ void Scatter3DRenderer::initializeOpenGL() loadBackgroundMesh(); } +void Scatter3DRenderer::fixCameraTarget(QVector3D &target) +{ + target.setX(target.x() * m_scaleX); + target.setY(target.y() * m_scaleY); + target.setZ(target.z() * -m_scaleZ); +} + void Scatter3DRenderer::updateData() { calculateSceneScalingFactors(); @@ -284,13 +290,6 @@ void Scatter3DRenderer::updateScene(Q3DScene *scene) { scene->activeCamera()->d_ptr->setMinYRotation(-90.0f); - if (m_hasHeightAdjustmentChanged) { - // Set initial camera position. Also update if height adjustment has changed. - scene->activeCamera()->d_ptr->setBaseOrientation(cameraDistanceVector, zeroVector, - upVector); - m_hasHeightAdjustmentChanged = false; - } - Abstract3DRenderer::updateScene(scene); } @@ -2066,6 +2065,9 @@ void Scatter3DRenderer::calculateSceneScalingFactors() m_axisCacheX.setTranslate(-m_scaleX); m_axisCacheY.setTranslate(-m_scaleY); m_axisCacheZ.setTranslate(m_scaleZ); + + updateCameraViewport(); + updateCustomItemPositions(); } void Scatter3DRenderer::initShaders(const QString &vertexShader, const QString &fragmentShader) @@ -2252,7 +2254,7 @@ QVector3D Scatter3DRenderer::convertPositionToTranslation(const QVector3D &posit } else { xTrans = position.x() * m_scaleX; yTrans = position.y() * m_scaleY; - zTrans = position.z() * m_scaleZ; + zTrans = position.z() * -m_scaleZ; } return QVector3D(xTrans, yTrans, zTrans); } diff --git a/src/datavisualization/engine/scatter3drenderer_p.h b/src/datavisualization/engine/scatter3drenderer_p.h index 9baad16a..5f82bf70 100644 --- a/src/datavisualization/engine/scatter3drenderer_p.h +++ b/src/datavisualization/engine/scatter3drenderer_p.h @@ -77,7 +77,6 @@ private: ScatterSeriesRenderCache *m_selectedSeriesCache; ScatterSeriesRenderCache *m_oldSelectedSeriesCache; GLfloat m_dotSizeScale; - bool m_hasHeightAdjustmentChanged; ScatterRenderItem m_dummyRenderItem; GLfloat m_maxItemSize; int m_clickedIndex; @@ -112,6 +111,7 @@ public slots: protected: virtual void initializeOpenGL(); + virtual void fixCameraTarget(QVector3D &target); private: virtual void initShaders(const QString &vertexShader, const QString &fragmentShader); diff --git a/src/datavisualization/engine/surface3drenderer.cpp b/src/datavisualization/engine/surface3drenderer.cpp index 83b5c7fb..1607f66a 100644 --- a/src/datavisualization/engine/surface3drenderer.cpp +++ b/src/datavisualization/engine/surface3drenderer.cpp @@ -66,7 +66,6 @@ Surface3DRenderer::Surface3DRenderer(Surface3DController *controller) m_flatSupported(true), m_selectionActive(false), m_shadowQualityMultiplier(3), - m_hasHeightAdjustmentChanged(true), m_selectedPoint(Surface3DController::invalidSelectionPosition()), m_selectedSeries(0), m_clickedPosition(Surface3DController::invalidSelectionPosition()), @@ -152,6 +151,13 @@ void Surface3DRenderer::initializeOpenGL() m_noShadowTexture = m_textureHelper->create2DTexture(image, false, true, false, true); } +void Surface3DRenderer::fixCameraTarget(QVector3D &target) +{ + target.setX(target.x() * m_scaleX); + target.setY(target.y() * m_scaleY); + target.setZ(target.z() * -m_scaleZ); +} + void Surface3DRenderer::updateData() { calculateSceneScalingFactors(); @@ -693,15 +699,6 @@ QRect Surface3DRenderer::calculateSampleRect(const QSurfaceDataArray &array) void Surface3DRenderer::updateScene(Q3DScene *scene) { - // Set initial camera position - // X must be 0 for rotation to work - we can use "setCameraRotation" for setting it later - if (m_hasHeightAdjustmentChanged) { - scene->activeCamera()->d_ptr->setBaseOrientation(cameraDistanceVector, zeroVector, - upVector); - // For now this is used just to make things once. Proper use will come - m_hasHeightAdjustmentChanged = false; - } - Abstract3DRenderer::updateScene(scene); if (m_selectionActive @@ -1111,7 +1108,7 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) / activeCamera->zoomLevel() / m_autoScaleAdjustment * 100.0f; qreal cameraAngle = qreal(activeCamera->yRotation()) / 180.0 * M_PI; float cameraYPos = float(qSin(cameraAngle)) * distanceToCenter; - m_yFlippedForGrid = cameraYPos < (m_scaleYWithBackground); + m_yFlippedForGrid = cameraYPos < (m_scaleYWithBackground - m_oldCameraTarget.y()); } else if (m_useOrthoProjection && activeCamera->yRotation() == 0.0f) { // With ortho we only need to flip at angle zero, to fix label autorotation angles m_yFlippedForGrid = !m_yFlipped; @@ -2517,6 +2514,9 @@ void Surface3DRenderer::calculateSceneScalingFactors() m_axisCacheX.setTranslate(-m_scaleX); m_axisCacheY.setTranslate(-m_scaleY); m_axisCacheZ.setTranslate(m_scaleZ); + + updateCameraViewport(); + updateCustomItemPositions(); } void Surface3DRenderer::checkFlatSupport(SurfaceSeriesRenderCache *cache) @@ -2962,7 +2962,7 @@ QVector3D Surface3DRenderer::convertPositionToTranslation(const QVector3D &posit } else { xTrans = position.x() * m_scaleX; yTrans = position.y() * m_scaleY; - zTrans = position.z() * m_scaleZ; + zTrans = position.z() * -m_scaleZ; } return QVector3D(xTrans, yTrans, zTrans); } diff --git a/src/datavisualization/engine/surface3drenderer_p.h b/src/datavisualization/engine/surface3drenderer_p.h index f34a927a..9c53757d 100644 --- a/src/datavisualization/engine/surface3drenderer_p.h +++ b/src/datavisualization/engine/surface3drenderer_p.h @@ -75,7 +75,6 @@ private: bool m_selectionActive; AbstractRenderItem m_dummyRenderItem; GLint m_shadowQualityMultiplier; - bool m_hasHeightAdjustmentChanged; QPoint m_selectedPoint; QSurface3DSeries *m_selectedSeries; QPoint m_clickedPosition; @@ -111,6 +110,7 @@ public: protected: void initializeOpenGL(); + virtual void fixCameraTarget(QVector3D &target); signals: void flatShadingSupportedChanged(bool supported); diff --git a/src/datavisualizationqml2/datavisualizationqml2_plugin.cpp b/src/datavisualizationqml2/datavisualizationqml2_plugin.cpp index c824afec..f5e2b4e0 100644 --- a/src/datavisualizationqml2/datavisualizationqml2_plugin.cpp +++ b/src/datavisualizationqml2/datavisualizationqml2_plugin.cpp @@ -114,6 +114,7 @@ void QtDataVisualizationQml2Plugin::registerTypes(const char *uri) qmlRegisterUncreatableType(uri, 1, 2, "AbstractGraph3D", QLatin1String("Trying to create uncreatable: AbstractGraph3D.")); qmlRegisterType(uri, 1, 2, "Surface3D"); + qmlRegisterType(uri, 1, 2, "Camera3D"); // New types qmlRegisterType(uri, 1, 2, "InputHandler3D"); diff --git a/tests/barstest/chart.cpp b/tests/barstest/chart.cpp index e0f9ff18..445c235e 100644 --- a/tests/barstest/chart.cpp +++ b/tests/barstest/chart.cpp @@ -1495,6 +1495,30 @@ void GraphModifier::handleFpsChange(qreal fps) m_fpsLabel->setText(fpsPrefix + QString::number(qRound(fps))); } +void GraphModifier::setCameraTargetX(int value) +{ + // Value is (-100, 100), normalize + m_cameraTarget.setX(float(value) / 100.0f); + m_graph->scene()->activeCamera()->setTarget(m_cameraTarget); + qDebug() << "m_cameraTarget:" << m_cameraTarget; +} + +void GraphModifier::setCameraTargetY(int value) +{ + // Value is (-100, 100), normalize + m_cameraTarget.setY(float(value) / 100.0f); + m_graph->scene()->activeCamera()->setTarget(m_cameraTarget); + qDebug() << "m_cameraTarget:" << m_cameraTarget; +} + +void GraphModifier::setCameraTargetZ(int value) +{ + // Value is (-100, 100), normalize + m_cameraTarget.setZ(float(value) / 100.0f); + m_graph->scene()->activeCamera()->setTarget(m_cameraTarget); + qDebug() << "m_cameraTarget:" << m_cameraTarget; +} + void GraphModifier::populateFlatSeries(QBar3DSeries *series, int rows, int columns, float value) { QBarDataArray *dataArray = new QBarDataArray; diff --git a/tests/barstest/chart.h b/tests/barstest/chart.h index 7ee781ad..6740df73 100644 --- a/tests/barstest/chart.h +++ b/tests/barstest/chart.h @@ -118,6 +118,9 @@ public slots: void triggerRotation(); void handleValueAxisLabelsChanged(); void handleFpsChange(qreal fps); + void setCameraTargetX(int value); + void setCameraTargetY(int value); + void setCameraTargetZ(int value); signals: void shadowQualityChanged(int quality); @@ -172,6 +175,7 @@ private: QTimer m_rotationTimer; QLabel *m_fpsLabel; QBar3DSeries *m_extraSeries; + QVector3D m_cameraTarget; }; #endif diff --git a/tests/barstest/main.cpp b/tests/barstest/main.cpp index 2d755c7f..bd891cd3 100644 --- a/tests/barstest/main.cpp +++ b/tests/barstest/main.cpp @@ -331,6 +331,22 @@ int main(int argc, char **argv) valueAxisSegmentsSpin->setMaximum(100); valueAxisSegmentsSpin->setValue(10); + QSlider *cameraTargetSliderX = new QSlider(Qt::Horizontal, widget); + cameraTargetSliderX->setTickInterval(1); + cameraTargetSliderX->setMinimum(-100); + cameraTargetSliderX->setValue(0); + cameraTargetSliderX->setMaximum(100); + QSlider *cameraTargetSliderY = new QSlider(Qt::Horizontal, widget); + cameraTargetSliderY->setTickInterval(1); + cameraTargetSliderY->setMinimum(-100); + cameraTargetSliderY->setValue(0); + cameraTargetSliderY->setMaximum(100); + QSlider *cameraTargetSliderZ = new QSlider(Qt::Horizontal, widget); + cameraTargetSliderZ->setTickInterval(1); + cameraTargetSliderZ->setMinimum(-100); + cameraTargetSliderZ->setValue(0); + cameraTargetSliderZ->setMaximum(100); + vLayout->addWidget(addSeriesButton, 0, Qt::AlignTop); vLayout->addWidget(addDataButton, 0, Qt::AlignTop); vLayout->addWidget(addMultiDataButton, 0, Qt::AlignTop); @@ -400,7 +416,11 @@ int main(int argc, char **argv) vLayout3->addWidget(new QLabel(QStringLiteral("Log axis base")), 0, Qt::AlignTop); vLayout3->addWidget(logBaseEdit, 0, Qt::AlignTop); vLayout3->addWidget(new QLabel(QStringLiteral("Value axis segments")), 0, Qt::AlignTop); - vLayout3->addWidget(valueAxisSegmentsSpin, 1, Qt::AlignTop); + vLayout3->addWidget(valueAxisSegmentsSpin, 0, Qt::AlignTop); + vLayout3->addWidget(new QLabel(QStringLiteral("Camera target")), 0, Qt::AlignTop); + vLayout3->addWidget(cameraTargetSliderX, 0, Qt::AlignTop); + vLayout3->addWidget(cameraTargetSliderY, 0, Qt::AlignTop); + vLayout3->addWidget(cameraTargetSliderZ, 1, Qt::AlignTop); // TODO: Add example for setMeshFileName widget->show(); @@ -429,6 +449,12 @@ int main(int argc, char **argv) &GraphModifier::setMinY); QObject::connect(maxSliderY, &QSlider::valueChanged, modifier, &GraphModifier::setMaxY); + QObject::connect(cameraTargetSliderX, &QSlider::valueChanged, modifier, + &GraphModifier::setCameraTargetX); + QObject::connect(cameraTargetSliderY, &QSlider::valueChanged, modifier, + &GraphModifier::setCameraTargetY); + QObject::connect(cameraTargetSliderZ, &QSlider::valueChanged, modifier, + &GraphModifier::setCameraTargetZ); QObject::connect(shadowQuality, SIGNAL(currentIndexChanged(int)), modifier, SLOT(changeShadowQuality(int))); diff --git a/tests/qmlcamera/qml/qmlcamera/main.qml b/tests/qmlcamera/qml/qmlcamera/main.qml index 444e175c..cb1737f6 100644 --- a/tests/qmlcamera/qml/qmlcamera/main.qml +++ b/tests/qmlcamera/qml/qmlcamera/main.qml @@ -18,7 +18,7 @@ import QtQuick 2.1 import QtQuick.Controls 1.0 -import QtDataVisualization 1.1 +import QtDataVisualization 1.2 import "." Rectangle { @@ -35,6 +35,15 @@ Rectangle { id: chartAxes } + Camera3D { + id: customCamera + wrapXRotation: false + xRotation: camControlArea.xValue + yRotation: camControlArea.yValue + zoomLevel: zoomSlider.value + target: Qt.vector3d(1.0, 1.0, 1.0) + } + Item { id: dataView width: parent.width - camControlArea.width @@ -60,21 +69,18 @@ Rectangle { columnAxis: chartAxes.column valueAxis: chartAxes.expenses - // Bind UI controls to the camera - scene.activeCamera.wrapXRotation: false - scene.activeCamera.xRotation: camControlArea.xValue - scene.activeCamera.yRotation: camControlArea.yValue - scene.activeCamera.zoomLevel: zoomSlider.value + scene.activeCamera: customCamera inputHandler: null customItemList: [shuttleItem, labelItem] + orthoProjection: true } Custom3DItem { id: shuttleItem meshFile: ":/items/shuttle.obj" textureFile: ":/items/shuttle.png" - position: Qt.vector3d(5.0,29.0,3.0) + position: Qt.vector3d(2.0,29.0,2.0) scaling: Qt.vector3d(0.2,0.2,0.2) } @@ -82,7 +88,7 @@ Rectangle { id: labelItem facingCamera: true positionAbsolute: true - position: Qt.vector3d(0.0,1.5,0.0) + position: Qt.vector3d(-1.0,1.5,-1.0) scaling: Qt.vector3d(1.0,1.0,1.0) text: "Qt Shuttle" } @@ -162,6 +168,11 @@ Rectangle { currentAngle += 5 chartData.series.meshAngle = currentAngle shuttleItem.setRotationAxisAndAngle(Qt.vector3d(0.0, 1.0, 1.0), currentAngle) + console.log("label pos:", labelItem.position) + labelItem.position.x += 0.1 + labelItem.position.z += 0.1 + customCamera.target.x -= 0.1 + customCamera.target.z -= 0.1 } } diff --git a/tests/scattertest/main.cpp b/tests/scattertest/main.cpp index 55515cb0..116a4dc2 100644 --- a/tests/scattertest/main.cpp +++ b/tests/scattertest/main.cpp @@ -41,6 +41,7 @@ int main(int argc, char **argv) QHBoxLayout *hLayout = new QHBoxLayout(widget); QVBoxLayout *vLayout = new QVBoxLayout(); QVBoxLayout *vLayout2 = new QVBoxLayout(); + QVBoxLayout *vLayout3 = new QVBoxLayout(); Q3DScatter *chart = new Q3DScatter(); QSize screenSize = chart->screen()->size(); @@ -56,6 +57,7 @@ int main(int argc, char **argv) hLayout->addWidget(container, 1); hLayout->addLayout(vLayout); hLayout->addLayout(vLayout2); + hLayout->addLayout(vLayout3); QPushButton *themeButton = new QPushButton(widget); themeButton->setText(QStringLiteral("Change theme")); @@ -276,6 +278,22 @@ int main(int argc, char **argv) radialLabelSlider->setValue(100); radialLabelSlider->setMaximum(150); + QSlider *cameraTargetSliderX = new QSlider(Qt::Horizontal, widget); + cameraTargetSliderX->setTickInterval(1); + cameraTargetSliderX->setMinimum(-100); + cameraTargetSliderX->setValue(0); + cameraTargetSliderX->setMaximum(100); + QSlider *cameraTargetSliderY = new QSlider(Qt::Horizontal, widget); + cameraTargetSliderY->setTickInterval(1); + cameraTargetSliderY->setMinimum(-100); + cameraTargetSliderY->setValue(0); + cameraTargetSliderY->setMaximum(100); + QSlider *cameraTargetSliderZ = new QSlider(Qt::Horizontal, widget); + cameraTargetSliderZ->setTickInterval(1); + cameraTargetSliderZ->setMinimum(-100); + cameraTargetSliderZ->setValue(0); + cameraTargetSliderZ->setMaximum(100); + vLayout->addWidget(themeButton, 0, Qt::AlignTop); vLayout->addWidget(labelButton, 0, Qt::AlignTop); vLayout->addWidget(styleButton, 0, Qt::AlignTop); @@ -326,15 +344,20 @@ int main(int argc, char **argv) vLayout2->addWidget(aspectRatioSlider); vLayout2->addWidget(new QLabel(QStringLiteral("Adjust horizontal aspect ratio"))); vLayout2->addWidget(horizontalAspectRatioSlider, 1, Qt::AlignTop); - vLayout2->addWidget(optimizationStaticCB); - vLayout2->addWidget(orthoCB); - vLayout2->addWidget(polarCB); - vLayout2->addWidget(axisTitlesVisibleCB); - vLayout2->addWidget(axisTitlesFixedCB); - vLayout2->addWidget(new QLabel(QStringLiteral("Axis label rotation"))); - vLayout2->addWidget(axisLabelRotationSlider); - vLayout2->addWidget(new QLabel(QStringLiteral("Radial label offset"))); - vLayout2->addWidget(radialLabelSlider, 1, Qt::AlignTop); + + vLayout3->addWidget(optimizationStaticCB); + vLayout3->addWidget(orthoCB); + vLayout3->addWidget(polarCB); + vLayout3->addWidget(axisTitlesVisibleCB); + vLayout3->addWidget(axisTitlesFixedCB); + vLayout3->addWidget(new QLabel(QStringLiteral("Axis label rotation"))); + vLayout3->addWidget(axisLabelRotationSlider); + vLayout3->addWidget(new QLabel(QStringLiteral("Radial label offset"))); + vLayout3->addWidget(radialLabelSlider, 0, Qt::AlignTop); + vLayout3->addWidget(new QLabel(QStringLiteral("Camera target")), 0, Qt::AlignTop); + vLayout3->addWidget(cameraTargetSliderX, 0, Qt::AlignTop); + vLayout3->addWidget(cameraTargetSliderY, 0, Qt::AlignTop); + vLayout3->addWidget(cameraTargetSliderZ, 1, Qt::AlignTop); ScatterDataModifier *modifier = new ScatterDataModifier(chart); @@ -440,6 +463,12 @@ int main(int argc, char **argv) &ScatterDataModifier::setHorizontalAspectRatio); QObject::connect(radialLabelSlider, &QSlider::valueChanged, modifier, &ScatterDataModifier::changeRadialLabelOffset); + QObject::connect(cameraTargetSliderX, &QSlider::valueChanged, modifier, + &ScatterDataModifier::setCameraTargetX); + QObject::connect(cameraTargetSliderY, &QSlider::valueChanged, modifier, + &ScatterDataModifier::setCameraTargetY); + QObject::connect(cameraTargetSliderZ, &QSlider::valueChanged, modifier, + &ScatterDataModifier::setCameraTargetZ); modifier->setFpsLabel(fpsLabel); diff --git a/tests/scattertest/scatterchart.cpp b/tests/scattertest/scatterchart.cpp index 5db772b5..35d56236 100644 --- a/tests/scattertest/scatterchart.cpp +++ b/tests/scattertest/scatterchart.cpp @@ -993,6 +993,30 @@ void ScatterDataModifier::toggleOrtho(bool enable) m_chart->setOrthoProjection(enable); } +void ScatterDataModifier::setCameraTargetX(int value) +{ + // Value is (-100, 100), normalize + m_cameraTarget.setX(float(value) / 100.0f); + m_chart->scene()->activeCamera()->setTarget(m_cameraTarget); + qDebug() << "m_cameraTarget:" << m_cameraTarget; +} + +void ScatterDataModifier::setCameraTargetY(int value) +{ + // Value is (-100, 100), normalize + m_cameraTarget.setY(float(value) / 100.0f); + m_chart->scene()->activeCamera()->setTarget(m_cameraTarget); + qDebug() << "m_cameraTarget:" << m_cameraTarget; +} + +void ScatterDataModifier::setCameraTargetZ(int value) +{ + // Value is (-100, 100), normalize + m_cameraTarget.setZ(float(value) / 100.0f); + m_chart->scene()->activeCamera()->setTarget(m_cameraTarget); + qDebug() << "m_cameraTarget:" << m_cameraTarget; +} + void ScatterDataModifier::changeShadowQuality(int quality) { QAbstract3DGraph::ShadowQuality sq = QAbstract3DGraph::ShadowQuality(quality); diff --git a/tests/scattertest/scatterchart.h b/tests/scattertest/scatterchart.h index df2494b6..1a97c5f0 100644 --- a/tests/scattertest/scatterchart.h +++ b/tests/scattertest/scatterchart.h @@ -98,6 +98,9 @@ public slots: void togglePolar(bool enable); void toggleStatic(bool enable); void toggleOrtho(bool enable); + void setCameraTargetX(int value); + void setCameraTargetY(int value); + void setCameraTargetZ(int value); signals: void shadowQualityChanged(int quality); @@ -118,6 +121,8 @@ private: QScatter3DSeries *m_targetSeries; QScatterDataArray m_massiveTestCacheArray; QLabel *m_fpsLabel; + QVector3D m_cameraTarget; + }; #endif diff --git a/tests/surfacetest/graphmodifier.cpp b/tests/surfacetest/graphmodifier.cpp index a685b71b..a22ede2b 100644 --- a/tests/surfacetest/graphmodifier.cpp +++ b/tests/surfacetest/graphmodifier.cpp @@ -793,6 +793,30 @@ void GraphModifier::togglePolar(bool enabled) m_graph->setPolar(enabled); } +void GraphModifier::setCameraTargetX(int value) +{ + // Value is (-100, 100), normalize + m_cameraTarget.setX(float(value) / 100.0f); + m_graph->scene()->activeCamera()->setTarget(m_cameraTarget); + qDebug() << "m_cameraTarget:" << m_cameraTarget; +} + +void GraphModifier::setCameraTargetY(int value) +{ + // Value is (-100, 100), normalize + m_cameraTarget.setY(float(value) / 100.0f); + m_graph->scene()->activeCamera()->setTarget(m_cameraTarget); + qDebug() << "m_cameraTarget:" << m_cameraTarget; +} + +void GraphModifier::setCameraTargetZ(int value) +{ + // Value is (-100, 100), normalize + m_cameraTarget.setZ(float(value) / 100.0f); + m_graph->scene()->activeCamera()->setTarget(m_cameraTarget); + qDebug() << "m_cameraTarget:" << m_cameraTarget; +} + void GraphModifier::resetArrayAndSliders(QSurfaceDataArray *array, float minZ, float maxZ, float minX, float maxX) { m_axisMinSliderX->setValue(minX); diff --git a/tests/surfacetest/graphmodifier.h b/tests/surfacetest/graphmodifier.h index 62145d45..f461f022 100644 --- a/tests/surfacetest/graphmodifier.h +++ b/tests/surfacetest/graphmodifier.h @@ -133,6 +133,9 @@ public slots: void toggleXAscending(bool enabled); void toggleZAscending(bool enabled); void togglePolar(bool enabled); + void setCameraTargetX(int value); + void setCameraTargetY(int value); + void setCameraTargetZ(int value); private: void fillSeries(); @@ -186,6 +189,7 @@ private: float m_multiSampleOffsetX[4]; float m_multiSampleOffsetZ[4]; QSurfaceDataArray m_massiveTestCacheArray; + QVector3D m_cameraTarget; }; #endif diff --git a/tests/surfacetest/main.cpp b/tests/surfacetest/main.cpp index 3a7307ea..f89e6c50 100644 --- a/tests/surfacetest/main.cpp +++ b/tests/surfacetest/main.cpp @@ -46,8 +46,10 @@ int main(int argc, char *argv[]) QHBoxLayout *hLayout = new QHBoxLayout(widget); QVBoxLayout *vLayout = new QVBoxLayout(); QVBoxLayout *vLayout2 = new QVBoxLayout(); + QVBoxLayout *vLayout3 = new QVBoxLayout(); vLayout->setAlignment(Qt::AlignTop); vLayout2->setAlignment(Qt::AlignTop); + vLayout3->setAlignment(Qt::AlignTop); Q3DSurface *surfaceGraph = new Q3DSurface(); QSize screenSize = surfaceGraph->screen()->size(); @@ -66,6 +68,7 @@ int main(int argc, char *argv[]) hLayout->addWidget(container, 1); hLayout->addLayout(vLayout); hLayout->addLayout(vLayout2); + hLayout->addLayout(vLayout3); QCheckBox *smoothCB = new QCheckBox(widget); smoothCB->setText(QStringLiteral("Flat Surface")); @@ -406,6 +409,22 @@ int main(int argc, char *argv[]) surfaceTextureCB->setText(QStringLiteral("Map texture")); surfaceTextureCB->setChecked(false); + QSlider *cameraTargetSliderX = new QSlider(Qt::Horizontal, widget); + cameraTargetSliderX->setTickInterval(1); + cameraTargetSliderX->setMinimum(-100); + cameraTargetSliderX->setValue(0); + cameraTargetSliderX->setMaximum(100); + QSlider *cameraTargetSliderY = new QSlider(Qt::Horizontal, widget); + cameraTargetSliderY->setTickInterval(1); + cameraTargetSliderY->setMinimum(-100); + cameraTargetSliderY->setValue(0); + cameraTargetSliderY->setMaximum(100); + QSlider *cameraTargetSliderZ = new QSlider(Qt::Horizontal, widget); + cameraTargetSliderZ->setTickInterval(1); + cameraTargetSliderZ->setMinimum(-100); + cameraTargetSliderZ->setValue(0); + cameraTargetSliderZ->setMaximum(100); + // Add controls to the layout #ifdef MULTI_SERIES vLayout->addWidget(series1CB); @@ -433,7 +452,7 @@ int main(int argc, char *argv[]) vLayout->addWidget(surfaceGridS4CB); vLayout->addWidget(surfaceS4CB); vLayout->addWidget(series4VisibleCB); - vLayout->addWidget(surfaceTextureCB); + vLayout->addWidget(surfaceTextureCB, 1, Qt::AlignTop); #endif #ifndef MULTI_SERIES vLayout->addWidget(new QLabel(QStringLiteral("Select surface sample"))); @@ -443,59 +462,65 @@ int main(int argc, char *argv[]) vLayout->addWidget(new QLabel(QStringLiteral("Adjust sample count"))); vLayout->addWidget(gridSlidersLockCB); vLayout->addWidget(gridSliderX); - vLayout->addWidget(gridSliderZ); + vLayout->addWidget(gridSliderZ, 1, Qt::AlignTop); #endif - vLayout->addWidget(new QLabel(QStringLiteral("Adjust vertical aspect ratio"))); - vLayout->addWidget(aspectRatioSlider); - vLayout->addWidget(new QLabel(QStringLiteral("Adjust horizontal aspect ratio"))); - vLayout->addWidget(horizontalAspectRatioSlider); - vLayout->addWidget(new QLabel(QStringLiteral("Adjust axis range"))); - vLayout->addWidget(axisRangeSliderX); - vLayout->addWidget(axisRangeSliderY); - vLayout->addWidget(axisRangeSliderZ); - vLayout->addWidget(new QLabel(QStringLiteral("Adjust axis minimum"))); - vLayout->addWidget(axisMinSliderX); - vLayout->addWidget(axisMinSliderY); - vLayout->addWidget(axisMinSliderZ); - vLayout->addWidget(xAscendingCB); - vLayout->addWidget(zAscendingCB); - vLayout->addWidget(polarCB); + + vLayout2->addWidget(new QLabel(QStringLiteral("Adjust vertical aspect ratio"))); + vLayout2->addWidget(aspectRatioSlider); + vLayout2->addWidget(new QLabel(QStringLiteral("Adjust horizontal aspect ratio"))); + vLayout2->addWidget(horizontalAspectRatioSlider); + vLayout2->addWidget(new QLabel(QStringLiteral("Adjust axis range"))); + vLayout2->addWidget(axisRangeSliderX); + vLayout2->addWidget(axisRangeSliderY); + vLayout2->addWidget(axisRangeSliderZ); + vLayout2->addWidget(new QLabel(QStringLiteral("Adjust axis minimum"))); + vLayout2->addWidget(axisMinSliderX); + vLayout2->addWidget(axisMinSliderY); + vLayout2->addWidget(axisMinSliderZ); + vLayout2->addWidget(xAscendingCB); + vLayout2->addWidget(zAscendingCB); + vLayout2->addWidget(polarCB); vLayout2->addWidget(new QLabel(QStringLiteral("Change font"))); vLayout2->addWidget(fontList); - vLayout2->addWidget(labelButton); - vLayout2->addWidget(meshButton); vLayout2->addWidget(new QLabel(QStringLiteral("Change theme"))); vLayout2->addWidget(themeList); vLayout2->addWidget(new QLabel(QStringLiteral("Adjust shadow quality"))); vLayout2->addWidget(shadowQuality); vLayout2->addWidget(new QLabel(QStringLiteral("Selection Mode"))); vLayout2->addWidget(selectionMode); + vLayout2->addWidget(new QLabel(QStringLiteral("Camera target"))); + vLayout2->addWidget(cameraTargetSliderX); + vLayout2->addWidget(cameraTargetSliderY); + vLayout2->addWidget(cameraTargetSliderZ, 1, Qt::AlignTop); + + vLayout3->addWidget(labelButton); + vLayout3->addWidget(meshButton); #ifndef MULTI_SERIES - vLayout2->addWidget(selectButton); - vLayout2->addWidget(selectionInfoLabel); - vLayout2->addWidget(flipViewsButton); + vLayout3->addWidget(selectButton); + vLayout3->addWidget(selectionInfoLabel); + vLayout3->addWidget(flipViewsButton); #endif - vLayout2->addWidget(colorPB); - vLayout2->addWidget(changeRowButton); - vLayout2->addWidget(changeRowsButton); - vLayout2->addWidget(changeMultipleRowsButton); - vLayout2->addWidget(changeItemButton); - vLayout2->addWidget(changeMultipleItemButton); - vLayout2->addWidget(addRowButton); - vLayout2->addWidget(addRowsButton); - vLayout2->addWidget(insertRowButton); - vLayout2->addWidget(insertRowsButton); - vLayout2->addWidget(removeRowButton); - vLayout2->addWidget(resetArrayButton); - vLayout2->addWidget(resetArrayEmptyButton); - vLayout2->addWidget(massiveDataTestButton); - vLayout2->addWidget(testReverseButton); - vLayout2->addWidget(testDataOrderingButton); - vLayout2->addWidget(axisTitlesVisibleCB); - vLayout2->addWidget(axisTitlesFixedCB); - vLayout2->addWidget(new QLabel(QStringLiteral("Axis label rotation"))); - vLayout2->addWidget(axisLabelRotationSlider, 1, Qt::AlignTop); + vLayout3->addWidget(colorPB); + vLayout3->addWidget(changeRowButton); + vLayout3->addWidget(changeRowsButton); + vLayout3->addWidget(changeMultipleRowsButton); + vLayout3->addWidget(changeItemButton); + vLayout3->addWidget(changeMultipleItemButton); + vLayout3->addWidget(addRowButton); + vLayout3->addWidget(addRowsButton); + vLayout3->addWidget(insertRowButton); + vLayout3->addWidget(insertRowsButton); + vLayout3->addWidget(removeRowButton); + vLayout3->addWidget(resetArrayButton); + vLayout3->addWidget(resetArrayEmptyButton); + vLayout3->addWidget(massiveDataTestButton); + vLayout3->addWidget(testReverseButton); + vLayout3->addWidget(testDataOrderingButton); + vLayout3->addWidget(axisTitlesVisibleCB); + vLayout3->addWidget(axisTitlesFixedCB); + vLayout3->addWidget(new QLabel(QStringLiteral("Axis label rotation"))); + vLayout3->addWidget(axisLabelRotationSlider, 1, Qt::AlignTop); widget->show(); @@ -676,6 +701,12 @@ int main(int argc, char *argv[]) modifier, &GraphModifier::setHorizontalAspectRatio); QObject::connect(surfaceTextureCB, &QCheckBox::stateChanged, modifier, &GraphModifier::setSurfaceTexture); + QObject::connect(cameraTargetSliderX, &QSlider::valueChanged, modifier, + &GraphModifier::setCameraTargetX); + QObject::connect(cameraTargetSliderY, &QSlider::valueChanged, modifier, + &GraphModifier::setCameraTargetY); + QObject::connect(cameraTargetSliderZ, &QSlider::valueChanged, modifier, + &GraphModifier::setCameraTargetZ); #ifdef MULTI_SERIES modifier->setSeries1CB(series1CB); -- cgit v1.2.3 From 1cbeea3d48a1d55f5eef0ee419d209a7a9625c3f Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Wed, 6 Aug 2014 12:52:34 +0300 Subject: Add zooming to selection to bars example MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Task-number: QTRD-3262 Change-Id: I96de8d97813cc82707a9b608127464ed4d6be6f7 Reviewed-by: Tomi Korpipää --- examples/datavisualization/bars/doc/src/bars.qdoc | 24 +++++ examples/datavisualization/bars/graphmodifier.cpp | 110 ++++++++++++++++++++++ examples/datavisualization/bars/graphmodifier.h | 10 ++ examples/datavisualization/bars/main.cpp | 6 ++ 4 files changed, 150 insertions(+) diff --git a/examples/datavisualization/bars/doc/src/bars.qdoc b/examples/datavisualization/bars/doc/src/bars.qdoc index 9717fe8a..1117376f 100644 --- a/examples/datavisualization/bars/doc/src/bars.qdoc +++ b/examples/datavisualization/bars/doc/src/bars.qdoc @@ -188,6 +188,30 @@ You can use the same method with \c SelectionSlice and \c SelectionItem flags, as long as you have either \c SelectionRow or \c SelectionColumn set as well. + \section1 Zooming to selection + + As an example of adjusting camera target we have implemented an animation of zooming to + selection via a button press. Animation initializations are done in the constructor: + + \snippet bars/graphmodifier.cpp 12 + + The function \c{GraphModifier::zoomToSelectedBar()} contains the rest of the functionality: + + \snippet bars/graphmodifier.cpp 11 + + The QPropertyAnimation \c m_animationCameraTarget targets Q3DCamera::target property, + which takes a value normalized to the range (-1, 1). We figure out where the selected bar + is relative to axes, and use that as the end value for \c{m_animationCameraTarget}: + + \snippet bars/graphmodifier.cpp 13 + \dots + \snippet bars/graphmodifier.cpp 14 + + Likewise, we want to angle the camera so that it always points approximately to the center of + the graph at the end of the animation: + + \snippet bars/graphmodifier.cpp 15 + \section1 Example contents */ diff --git a/examples/datavisualization/bars/graphmodifier.cpp b/examples/datavisualization/bars/graphmodifier.cpp index 9c280bfb..656cee90 100644 --- a/examples/datavisualization/bars/graphmodifier.cpp +++ b/examples/datavisualization/bars/graphmodifier.cpp @@ -26,6 +26,7 @@ #include #include #include +#include using namespace QtDataVisualization; @@ -106,6 +107,39 @@ GraphModifier::GraphModifier(Q3DBars *bargraph) //! [9] resetTemperatureData(); //! [9] + + // Set up property animations for zooming to the selected bar + //! [12] + Q3DCamera *camera = m_graph->scene()->activeCamera(); + m_defaultAngleX = camera->xRotation(); + m_defaultAngleY = camera->yRotation(); + m_defaultZoom = camera->zoomLevel(); + m_defaultTarget = camera->target(); + + m_animationCameraX.setTargetObject(camera); + m_animationCameraY.setTargetObject(camera); + m_animationCameraZoom.setTargetObject(camera); + m_animationCameraTarget.setTargetObject(camera); + + m_animationCameraX.setPropertyName("xRotation"); + m_animationCameraY.setPropertyName("yRotation"); + m_animationCameraZoom.setPropertyName("zoomLevel"); + m_animationCameraTarget.setPropertyName("target"); + + int duration = 1700; + m_animationCameraX.setDuration(duration); + m_animationCameraY.setDuration(duration); + m_animationCameraZoom.setDuration(duration); + m_animationCameraTarget.setDuration(duration); + + // The zoom always first zooms out above the graph and then zooms in + qreal zoomOutFraction = 0.3; + m_animationCameraX.setKeyValueAt(zoomOutFraction, QVariant::fromValue(0.0f)); + m_animationCameraY.setKeyValueAt(zoomOutFraction, QVariant::fromValue(90.0f)); + m_animationCameraZoom.setKeyValueAt(zoomOutFraction, QVariant::fromValue(50.0f)); + m_animationCameraTarget.setKeyValueAt(zoomOutFraction, + QVariant::fromValue(QVector3D(0.0f, 0.0f, 0.0f))); + //! [12] } //! [0] @@ -187,6 +221,14 @@ void GraphModifier::changeStyle(int style) void GraphModifier::changePresetCamera() { + m_animationCameraX.stop(); + m_animationCameraY.stop(); + m_animationCameraZoom.stop(); + m_animationCameraTarget.stop(); + + // Restore camera target in case animation has changed it + m_graph->scene()->activeCamera()->setTarget(QVector3D(0.0f, 0.0f, 0.0f)); + //! [10] static int preset = Q3DCamera::CameraPresetFront; @@ -263,6 +305,74 @@ void GraphModifier::setAxisTitleFixed(bool enabled) m_yearAxis->setTitleFixed(enabled); } +//! [11] +void GraphModifier::zoomToSelectedBar() +{ + m_animationCameraX.stop(); + m_animationCameraY.stop(); + m_animationCameraZoom.stop(); + m_animationCameraTarget.stop(); + + Q3DCamera *camera = m_graph->scene()->activeCamera(); + float currentX = camera->xRotation(); + float currentY = camera->yRotation(); + float currentZoom = camera->zoomLevel(); + QVector3D currentTarget = camera->target(); + + m_animationCameraX.setStartValue(QVariant::fromValue(currentX)); + m_animationCameraY.setStartValue(QVariant::fromValue(currentY)); + m_animationCameraZoom.setStartValue(QVariant::fromValue(currentZoom)); + m_animationCameraTarget.setStartValue(QVariant::fromValue(currentTarget)); + + QPoint selectedBar = m_graph->selectedSeries() + ? m_graph->selectedSeries()->selectedBar() + : QBar3DSeries::invalidSelectionPosition(); + + if (selectedBar != QBar3DSeries::invalidSelectionPosition()) { + // Normalize selected bar position within axis range to determine target coordinates + //! [13] + QVector3D endTarget; + float xMin = m_graph->columnAxis()->min(); + float xRange = m_graph->columnAxis()->max() - xMin; + float zMin = m_graph->rowAxis()->min(); + float zRange = m_graph->rowAxis()->max() - zMin; + endTarget.setX((selectedBar.y() - xMin) / xRange * 2.0f - 1.0f); + endTarget.setZ((selectedBar.x() - zMin) / zRange * 2.0f - 1.0f); + //! [13] + + // Rotate the camera so that it always points approximately to the graph center + //! [15] + qreal endAngleX = qAtan(qreal(endTarget.z() / endTarget.x())) / M_PI * -180.0 + 90.0; + if (endTarget.x() > 0.0f) + endAngleX -= 180.0f; + float barValue = m_graph->selectedSeries()->dataProxy()->itemAt(selectedBar.x(), + selectedBar.y())->value(); + float endAngleY = barValue >= 0.0f ? 30.0f : -30.0f; + if (m_graph->valueAxis()->reversed()) + endAngleY *= -1.0f; + //! [15] + + m_animationCameraX.setEndValue(QVariant::fromValue(float(endAngleX))); + m_animationCameraY.setEndValue(QVariant::fromValue(endAngleY)); + m_animationCameraZoom.setEndValue(QVariant::fromValue(250)); + //! [14] + m_animationCameraTarget.setEndValue(QVariant::fromValue(endTarget)); + //! [14] + } else { + // No selected bar, so return to the default view + m_animationCameraX.setEndValue(QVariant::fromValue(m_defaultAngleX)); + m_animationCameraY.setEndValue(QVariant::fromValue(m_defaultAngleY)); + m_animationCameraZoom.setEndValue(QVariant::fromValue(m_defaultZoom)); + m_animationCameraTarget.setEndValue(QVariant::fromValue(m_defaultTarget)); + } + + m_animationCameraX.start(); + m_animationCameraY.start(); + m_animationCameraZoom.start(); + m_animationCameraTarget.start(); +} +//! [11] + void GraphModifier::changeShadowQuality(int quality) { QAbstract3DGraph::ShadowQuality sq = QAbstract3DGraph::ShadowQuality(quality); diff --git a/examples/datavisualization/bars/graphmodifier.h b/examples/datavisualization/bars/graphmodifier.h index 107ffbab..252af1b8 100644 --- a/examples/datavisualization/bars/graphmodifier.h +++ b/examples/datavisualization/bars/graphmodifier.h @@ -27,6 +27,7 @@ #include #include #include +#include using namespace QtDataVisualization; @@ -60,6 +61,7 @@ public slots: void changeLabelRotation(int rotation); void setAxisTitleVisibility(bool enabled); void setAxisTitleFixed(bool enabled); + void zoomToSelectedBar(); signals: void shadowQualityChanged(int quality); @@ -86,6 +88,14 @@ private: QBar3DSeries *m_secondarySeries; QAbstract3DSeries::Mesh m_barMesh; bool m_smooth; + QPropertyAnimation m_animationCameraX; + QPropertyAnimation m_animationCameraY; + QPropertyAnimation m_animationCameraZoom; + QPropertyAnimation m_animationCameraTarget; + float m_defaultAngleX; + float m_defaultAngleY; + float m_defaultZoom; + QVector3D m_defaultTarget; }; #endif diff --git a/examples/datavisualization/bars/main.cpp b/examples/datavisualization/bars/main.cpp index 96abdc51..0173f81c 100644 --- a/examples/datavisualization/bars/main.cpp +++ b/examples/datavisualization/bars/main.cpp @@ -84,6 +84,9 @@ int main(int argc, char **argv) QPushButton *cameraButton = new QPushButton(widget); cameraButton->setText(QStringLiteral("Change camera preset")); + QPushButton *zoomToSelectedButton = new QPushButton(widget); + zoomToSelectedButton->setText(QStringLiteral("Zoom to selected bar")); + QComboBox *selectionModeList = new QComboBox(widget); selectionModeList->addItem(QStringLiteral("None"), int(QAbstract3DGraph::SelectionNone)); @@ -206,6 +209,7 @@ int main(int argc, char **argv) //! [5] vLayout->addWidget(labelButton, 0, Qt::AlignTop); vLayout->addWidget(cameraButton, 0, Qt::AlignTop); + vLayout->addWidget(zoomToSelectedButton, 0, Qt::AlignTop); vLayout->addWidget(backgroundCheckBox); vLayout->addWidget(gridCheckBox); vLayout->addWidget(smoothCheckBox); @@ -243,6 +247,8 @@ int main(int argc, char **argv) &GraphModifier::changeLabelBackground); QObject::connect(cameraButton, &QPushButton::clicked, modifier, &GraphModifier::changePresetCamera); + QObject::connect(zoomToSelectedButton, &QPushButton::clicked, modifier, + &GraphModifier::zoomToSelectedBar); QObject::connect(backgroundCheckBox, &QCheckBox::stateChanged, modifier, &GraphModifier::setBackgroundEnabled); -- cgit v1.2.3 From d8037e46600ba65869eb72ed6a6a1b21ad966f5c Mon Sep 17 00:00:00 2001 From: Mika Salmela Date: Tue, 12 Aug 2014 14:12:17 +0300 Subject: Textured surface example Documentation once the feature set is final and code looking good. Size of image files adjusted when example area is locked. Change-Id: I7ff249ec3452d412a64ce029b33f7607965ffb3a Reviewed-by: Miikka Heikkinen --- examples/datavisualization/datavisualization.pro | 3 +- .../texturesurface/custominputhandler.cpp | 178 +++++++++++++++++++++ .../texturesurface/custominputhandler.h | 91 +++++++++++ .../texturesurface/doc/src/texturesurface.qdoc | 26 +++ .../texturesurface/highlightseries.cpp | 91 +++++++++++ .../texturesurface/highlightseries.h | 49 ++++++ examples/datavisualization/texturesurface/main.cpp | 64 ++++++++ .../texturesurface/maptexture.jpg | Bin 0 -> 352922 bytes .../texturesurface/surfacegraph.cpp | 78 +++++++++ .../texturesurface/surfacegraph.h | 52 ++++++ .../texturesurface/texturedsurface.qrc | 6 + .../texturesurface/texturesurface.pro | 25 +++ .../texturesurface/topographicseries.cpp | 69 ++++++++ .../texturesurface/topographicseries.h | 45 ++++++ .../texturesurface/topography.png | Bin 0 -> 395504 bytes 15 files changed, 776 insertions(+), 1 deletion(-) create mode 100644 examples/datavisualization/texturesurface/custominputhandler.cpp create mode 100644 examples/datavisualization/texturesurface/custominputhandler.h create mode 100644 examples/datavisualization/texturesurface/doc/src/texturesurface.qdoc create mode 100644 examples/datavisualization/texturesurface/highlightseries.cpp create mode 100644 examples/datavisualization/texturesurface/highlightseries.h create mode 100644 examples/datavisualization/texturesurface/main.cpp create mode 100644 examples/datavisualization/texturesurface/maptexture.jpg create mode 100644 examples/datavisualization/texturesurface/surfacegraph.cpp create mode 100644 examples/datavisualization/texturesurface/surfacegraph.h create mode 100644 examples/datavisualization/texturesurface/texturedsurface.qrc create mode 100644 examples/datavisualization/texturesurface/texturesurface.pro create mode 100644 examples/datavisualization/texturesurface/topographicseries.cpp create mode 100644 examples/datavisualization/texturesurface/topographicseries.h create mode 100644 examples/datavisualization/texturesurface/topography.png diff --git a/examples/datavisualization/datavisualization.pro b/examples/datavisualization/datavisualization.pro index c078630d..cb861b81 100644 --- a/examples/datavisualization/datavisualization.pro +++ b/examples/datavisualization/datavisualization.pro @@ -20,7 +20,8 @@ SUBDIRS += qmlbars \ surface \ rotations \ draggableaxes \ - customitems + customitems \ + texturesurface } qtHaveModule(multimedia):!android:!ios: SUBDIRS += audiolevels diff --git a/examples/datavisualization/texturesurface/custominputhandler.cpp b/examples/datavisualization/texturesurface/custominputhandler.cpp new file mode 100644 index 00000000..9072a0b4 --- /dev/null +++ b/examples/datavisualization/texturesurface/custominputhandler.cpp @@ -0,0 +1,178 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +#include "custominputhandler.h" + +#include +#include + +#include + +CustomInputHandler::CustomInputHandler(QAbstract3DGraph *graph, QObject *parent) : + Q3DInputHandler(parent), + m_highlight(0), + m_mousePressed(false), + m_state(StateNormal), + m_axisX(0), + m_axisZ(0), + m_speedModifier(20.0f) +{ + // Connect to the item selection signal from graph + connect(graph, &QAbstract3DGraph::selectedElementChanged, this, + &CustomInputHandler::handleElementSelected); +} + +void CustomInputHandler::mousePressEvent(QMouseEvent *event, const QPoint &mousePos) +{ + if (Qt::LeftButton == event->button()) { + m_highlight->setVisible(false); + m_mousePressed = true; + } + Q3DInputHandler::mousePressEvent(event, mousePos); +} + +void CustomInputHandler::wheelEvent(QWheelEvent *event) +{ + float delta = float(event->delta()); + + m_axisXMinValue += delta; + m_axisXMaxValue -= delta; + m_axisZMinValue += delta; + m_axisZMaxValue -= delta; + checkConstraints(); + + float y = (m_axisXMaxValue - m_axisXMinValue) * m_aspectRatio; + + m_axisX->setRange(m_axisXMinValue, m_axisXMaxValue); + m_axisY->setRange(100.0f, y); + m_axisZ->setRange(m_axisZMinValue, m_axisZMaxValue); +} + +void CustomInputHandler::mouseMoveEvent(QMouseEvent *event, const QPoint &mousePos) +{ + // Check if we're trying to drag axis label + if (m_mousePressed && m_state != StateNormal) { + setPreviousInputPos(inputPosition()); + setInputPosition(mousePos); + handleAxisDragging(); + } else { + Q3DInputHandler::mouseMoveEvent(event, mousePos); + } +} + +void CustomInputHandler::mouseReleaseEvent(QMouseEvent *event, const QPoint &mousePos) +{ + Q3DInputHandler::mouseReleaseEvent(event, mousePos); + m_mousePressed = false; + m_state = StateNormal; +} + +void CustomInputHandler::handleElementSelected(QAbstract3DGraph::ElementType type) +{ + switch (type) { + case QAbstract3DGraph::ElementAxisXLabel: + m_state = StateDraggingX; + break; + case QAbstract3DGraph::ElementAxisZLabel: + m_state = StateDraggingZ; + break; + default: + m_state = StateNormal; + break; + } +} + +void CustomInputHandler::handleAxisDragging() +{ + float distance = 0.0f; + + // Get scene orientation from active camera + float xRotation = scene()->activeCamera()->xRotation(); + + // Calculate directional drag multipliers based on rotation + float xMulX = qCos(qDegreesToRadians(xRotation)); + float xMulY = qSin(qDegreesToRadians(xRotation)); + float zMulX = qSin(qDegreesToRadians(xRotation)); + float zMulY = qCos(qDegreesToRadians(xRotation)); + + // Get the drag amount + QPoint move = inputPosition() - previousInputPos(); + + // Adjust axes + switch (m_state) { + case StateDraggingX: + distance = (move.x() * xMulX - move.y() * xMulY) * m_speedModifier; + m_axisXMinValue -= distance; + m_axisXMaxValue -= distance; + if (m_axisXMinValue < m_areaMinValue) { + float dist = m_axisXMaxValue - m_axisXMinValue; + m_axisXMinValue = m_areaMinValue; + m_axisXMaxValue = m_axisXMinValue + dist; + } + if (m_axisXMaxValue > m_areaMaxValue) { + float dist = m_axisXMaxValue - m_axisXMinValue; + m_axisXMaxValue = m_areaMaxValue; + m_axisXMinValue = m_axisXMaxValue - dist; + } + m_axisX->setRange(m_axisXMinValue, m_axisXMaxValue); + break; + case StateDraggingZ: + distance = (move.x() * zMulX + move.y() * zMulY) * m_speedModifier; + m_axisZMinValue += distance; + m_axisZMaxValue += distance; + if (m_axisZMinValue < m_areaMinValue) { + float dist = m_axisZMaxValue - m_axisZMinValue; + m_axisZMinValue = m_areaMinValue; + m_axisZMaxValue = m_axisZMinValue + dist; + } + if (m_axisZMaxValue > m_areaMaxValue) { + float dist = m_axisZMaxValue - m_axisZMinValue; + m_axisZMaxValue = m_areaMaxValue; + m_axisZMinValue = m_axisZMaxValue - dist; + } + m_axisZ->setRange(m_axisZMinValue, m_axisZMaxValue); + break; + default: + break; + } +} + +void CustomInputHandler::checkConstraints() +{ + if (m_axisXMinValue < m_areaMinValue) + m_axisXMinValue = m_areaMinValue; + if (m_axisXMaxValue > m_areaMaxValue) + m_axisXMaxValue = m_areaMaxValue; + // Don't allow too much zoom in + if ((m_axisXMaxValue - m_axisXMinValue) < m_axisXMinRange) { + float adjust = (m_axisXMinRange - (m_axisXMaxValue - m_axisXMinValue)) / 2.0f; + m_axisXMinValue -= adjust; + m_axisXMaxValue += adjust; + } + + if (m_axisZMinValue < m_areaMinValue) + m_axisZMinValue = m_areaMinValue; + if (m_axisZMaxValue > m_areaMaxValue) + m_axisZMaxValue = m_areaMaxValue; + // Don't allow too much zoom in + if ((m_axisZMaxValue - m_axisZMinValue) < m_axisZMinRange) { + float adjust = (m_axisZMinRange - (m_axisZMaxValue - m_axisZMinValue)) / 2.0f; + m_axisZMinValue -= adjust; + m_axisZMaxValue += adjust; + } +} diff --git a/examples/datavisualization/texturesurface/custominputhandler.h b/examples/datavisualization/texturesurface/custominputhandler.h new file mode 100644 index 00000000..5307a6be --- /dev/null +++ b/examples/datavisualization/texturesurface/custominputhandler.h @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +#ifndef CUSTOMINPUTHANDLER_H +#define CUSTOMINPUTHANDLER_H + +#include +#include +#include +#include "highlightseries.h" + +using namespace QtDataVisualization; + +class CustomInputHandler : public Q3DInputHandler +{ + Q_OBJECT + + enum InputState { + StateNormal = 0, + StateDraggingX, + StateDraggingZ, + StateDraggingY + }; + +public: + explicit CustomInputHandler(QAbstract3DGraph *graph, QObject *parent = 0); + + inline void setLimits(float min, float max) { + m_areaMinValue = min; + m_areaMaxValue = max; + m_axisXMinValue = m_areaMinValue; + m_axisXMaxValue = m_areaMaxValue; + m_axisZMinValue = m_areaMinValue; + m_axisZMaxValue = m_areaMaxValue; + m_axisXMinRange = (m_areaMaxValue - m_areaMinValue) * 0.49f; + m_axisZMinRange = (m_areaMaxValue - m_areaMinValue) * 0.49f; + } + inline void setAxes(QValue3DAxis *axisX, QValue3DAxis *axisY, QValue3DAxis *axisZ) { + m_axisX = axisX; + m_axisY = axisY; + m_axisZ = axisZ; + } + inline void setAspectRatio(float ratio) { m_aspectRatio = ratio; } + inline void setHighlightSeries(HighlightSeries *series) { m_highlight = series; } + inline void setDragSpeedModifier(float modifier) { m_speedModifier = modifier; } + + virtual void mousePressEvent(QMouseEvent *event, const QPoint &mousePos); + virtual void mouseMoveEvent(QMouseEvent *event, const QPoint &mousePos); + virtual void mouseReleaseEvent(QMouseEvent *event, const QPoint &mousePos); + virtual void wheelEvent(QWheelEvent *event); + +private: + void handleElementSelected(QAbstract3DGraph::ElementType type); + void handleAxisDragging(); + void checkConstraints(); + +private: + HighlightSeries *m_highlight; + bool m_mousePressed; + InputState m_state; + QValue3DAxis *m_axisX; + QValue3DAxis *m_axisY; + QValue3DAxis *m_axisZ; + float m_speedModifier; + float m_aspectRatio; + float m_axisXMinValue; + float m_axisXMaxValue; + float m_axisXMinRange; + float m_axisZMinValue; + float m_axisZMaxValue; + float m_axisZMinRange; + float m_areaMinValue; + float m_areaMaxValue; +}; + +#endif diff --git a/examples/datavisualization/texturesurface/doc/src/texturesurface.qdoc b/examples/datavisualization/texturesurface/doc/src/texturesurface.qdoc new file mode 100644 index 00000000..ee93dd0a --- /dev/null +++ b/examples/datavisualization/texturesurface/doc/src/texturesurface.qdoc @@ -0,0 +1,26 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +/*! + \example texturesurface + \title Texture Surface Example + \ingroup qtdatavisualization_examples + \brief Using texture with Q3DSurface. + + TODO +*/ diff --git a/examples/datavisualization/texturesurface/highlightseries.cpp b/examples/datavisualization/texturesurface/highlightseries.cpp new file mode 100644 index 00000000..3584d1d4 --- /dev/null +++ b/examples/datavisualization/texturesurface/highlightseries.cpp @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +#include "highlightseries.h" + +#include + +using namespace QtDataVisualization; + +HighlightSeries::HighlightSeries() + : m_width(100), + m_height(100) +{ + setDrawMode(QSurface3DSeries::DrawSurface); + setFlatShadingEnabled(true); + setVisible(false); +} + +HighlightSeries::~HighlightSeries() +{ +} + +void HighlightSeries::setTopographicSeries(TopographicSeries *series) +{ + m_topographicSeries = series; + m_srcWidth = m_topographicSeries->dataProxy()->array()->at(0)->size(); + m_srcHeight = m_topographicSeries->dataProxy()->array()->size(); + + QObject::connect(m_topographicSeries, &QSurface3DSeries::selectedPointChanged, + this, &HighlightSeries::handlePositionChange); +} + +void HighlightSeries::handlePositionChange(const QPoint &position) +{ + m_position = position; + + if (position == invalidSelectionPosition()) { + setVisible(false); + + return; + } + + int halfWidth = m_width / 2; + int halfHeight = m_height / 2; + + int startX = position.y() - halfWidth; + if (startX < 0 ) + startX = 0; + int endX = position.y() + halfWidth; + if (endX > (m_srcWidth - 1)) + endX = m_srcWidth - 1; + int startZ = position.x() - halfHeight; + if (startZ < 0 ) + startZ = 0; + int endZ = position.x() + halfHeight; + if (endZ > (m_srcHeight - 1)) + endZ = m_srcHeight - 1; + + QSurfaceDataProxy *srcProxy = m_topographicSeries->dataProxy(); + const QSurfaceDataArray &srcArray = *srcProxy->array(); + + QSurfaceDataArray *dataArray = new QSurfaceDataArray; + dataArray->reserve(endZ - startZ); + for (int i = startZ; i < endZ; i++) { + QSurfaceDataRow *newRow = new QSurfaceDataRow(endX - startX); + QSurfaceDataRow *srcRow = srcArray.at(i); + for (int j = startX, p = 0; j < endX; j++, p++) { + QVector3D pos = srcRow->at(j).position(); + (*newRow)[p].setPosition(QVector3D(pos.x(), pos.y() + 0.1f, pos.z())); + } + *dataArray << newRow; + } + + dataProxy()->resetArray(dataArray); + setVisible(true); +} diff --git a/examples/datavisualization/texturesurface/highlightseries.h b/examples/datavisualization/texturesurface/highlightseries.h new file mode 100644 index 00000000..36361fbc --- /dev/null +++ b/examples/datavisualization/texturesurface/highlightseries.h @@ -0,0 +1,49 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +#ifndef HIGHLIGHTSERIES_H +#define HIGHLIGHTSERIES_H + +#include + +#include "topographicseries.h" + +using namespace QtDataVisualization; + +class HighlightSeries : public QSurface3DSeries +{ + Q_OBJECT +public: + explicit HighlightSeries(); + ~HighlightSeries(); + + void setTopographicSeries(TopographicSeries *series); + +public slots: + void handlePositionChange(const QPoint &position); + +private: + int m_width; + int m_height; + int m_srcWidth; + int m_srcHeight; + QPoint m_position; + TopographicSeries *m_topographicSeries; +}; + +#endif // HIGHLIGHTSERIES_H diff --git a/examples/datavisualization/texturesurface/main.cpp b/examples/datavisualization/texturesurface/main.cpp new file mode 100644 index 00000000..e73dc864 --- /dev/null +++ b/examples/datavisualization/texturesurface/main.cpp @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +#include "surfacegraph.h" + +#include +#include +#include +#include +#include +#include + +int main(int argc, char **argv) +{ + QApplication app(argc, argv); + Q3DSurface *graph = new Q3DSurface(); + QWidget *container = QWidget::createWindowContainer(graph); + + QSize screenSize = graph->screen()->size(); + container->setMinimumSize(QSize(screenSize.width() / 2, screenSize.height() / 1.6)); + container->setMaximumSize(screenSize); + container->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + container->setFocusPolicy(Qt::StrongFocus); + + QWidget *widget = new QWidget; + QHBoxLayout *hLayout = new QHBoxLayout(widget); + QVBoxLayout *vLayout = new QVBoxLayout(); + hLayout->addWidget(container, 1); + hLayout->addLayout(vLayout); + vLayout->setAlignment(Qt::AlignTop); + + widget->setWindowTitle(QStringLiteral("Textured surface example")); + + QCheckBox *enableTexture = new QCheckBox(widget); + enableTexture->setText(QStringLiteral("Surface texture")); + + vLayout->addWidget(enableTexture); + + widget->show(); + + SurfaceGraph *modifier = new SurfaceGraph(graph); + + QObject::connect(enableTexture, &QCheckBox::stateChanged, + modifier, &SurfaceGraph::toggleSurfaceTexture); + + enableTexture->setChecked(true); + + return app.exec(); +} diff --git a/examples/datavisualization/texturesurface/maptexture.jpg b/examples/datavisualization/texturesurface/maptexture.jpg new file mode 100644 index 00000000..ae5d66eb Binary files /dev/null and b/examples/datavisualization/texturesurface/maptexture.jpg differ diff --git a/examples/datavisualization/texturesurface/surfacegraph.cpp b/examples/datavisualization/texturesurface/surfacegraph.cpp new file mode 100644 index 00000000..b8e92988 --- /dev/null +++ b/examples/datavisualization/texturesurface/surfacegraph.cpp @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +#include "surfacegraph.h" +#include "topographicseries.h" + +#include +#include + +#include + +using namespace QtDataVisualization; + +const float areaWidth = 8000.0f; +const float areaHeight = 8000.0f; +const float aspectRatio = 0.1389f; + +SurfaceGraph::SurfaceGraph(Q3DSurface *surface) + : m_graph(surface) +{ + m_graph->setAxisX(new QValue3DAxis); + m_graph->setAxisY(new QValue3DAxis); + m_graph->setAxisZ(new QValue3DAxis); + m_graph->axisX()->setLabelFormat("%i"); + m_graph->axisZ()->setLabelFormat("%i"); + m_graph->axisX()->setRange(0.0f, areaWidth); + m_graph->axisY()->setRange(100.0f, areaWidth * aspectRatio); + m_graph->axisZ()->setRange(0.0f, areaHeight); + m_graph->axisX()->setLabelAutoRotation(30); + m_graph->axisY()->setLabelAutoRotation(90); + m_graph->axisZ()->setLabelAutoRotation(30); + + m_topography = new TopographicSeries(); + m_topography->setTopographyFile(":/maps/topography", areaWidth, areaHeight); + m_topography->setItemLabelFormat(QStringLiteral("@yLabel m")); + + m_highlight = new HighlightSeries(); + m_highlight->setTopographicSeries(m_topography); + + m_graph->addSeries(m_topography); + m_graph->addSeries(m_highlight); + + m_inputHandler = new CustomInputHandler(m_graph); + m_inputHandler->setHighlightSeries(m_highlight); + m_inputHandler->setAxes(m_graph->axisX(), m_graph->axisY(), m_graph->axisZ()); + m_inputHandler->setLimits(0.0f, areaWidth); + m_inputHandler->setAspectRatio(aspectRatio); + + m_graph->setActiveInputHandler(m_inputHandler); +} + +SurfaceGraph::~SurfaceGraph() +{ + delete m_graph; +} + +void SurfaceGraph::toggleSurfaceTexture(bool enable) +{ + if (enable) + m_topography->setTextureFile(":/maps/maptexture"); + else + m_topography->setTextureFile(""); +} diff --git a/examples/datavisualization/texturesurface/surfacegraph.h b/examples/datavisualization/texturesurface/surfacegraph.h new file mode 100644 index 00000000..c1d81595 --- /dev/null +++ b/examples/datavisualization/texturesurface/surfacegraph.h @@ -0,0 +1,52 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +#ifndef SURFACEGRAPH_H +#define SURFACEGRAPH_H + +#include +#include +#include +#include "topographicseries.h" +#include "highlightseries.h" + +#include "custominputhandler.h" + +using namespace QtDataVisualization; + +class SurfaceGraph : public QObject +{ + Q_OBJECT +public: + explicit SurfaceGraph(Q3DSurface *surface); + ~SurfaceGraph(); + + void toggleSurfaceTexture(bool enable); + +private: + Q3DSurface *m_graph; + + TopographicSeries *m_topography; + HighlightSeries *m_highlight; + int m_highlightWidth; + int m_highlightHeight; + + CustomInputHandler *m_inputHandler; +}; + +#endif // SURFACEGRAPH_H diff --git a/examples/datavisualization/texturesurface/texturedsurface.qrc b/examples/datavisualization/texturesurface/texturedsurface.qrc new file mode 100644 index 00000000..94b96d24 --- /dev/null +++ b/examples/datavisualization/texturesurface/texturedsurface.qrc @@ -0,0 +1,6 @@ + + + topography.png + maptexture.jpg + + diff --git a/examples/datavisualization/texturesurface/texturesurface.pro b/examples/datavisualization/texturesurface/texturesurface.pro new file mode 100644 index 00000000..f24c3d17 --- /dev/null +++ b/examples/datavisualization/texturesurface/texturesurface.pro @@ -0,0 +1,25 @@ +android|ios { + error( "This example is not supported for android or ios." ) +} + +!include( ../examples.pri ) { + error( "Couldn't find the examples.pri file!" ) +} + +SOURCES += main.cpp \ + surfacegraph.cpp \ + topographicseries.cpp \ + highlightseries.cpp \ + custominputhandler.cpp + +HEADERS += surfacegraph.h \ + topographicseries.h \ + highlightseries.h \ + custominputhandler.h + +QT += widgets + +RESOURCES += texturedsurface.qrc + +OTHER_FILES += doc/src/* \ + doc/images/* diff --git a/examples/datavisualization/texturesurface/topographicseries.cpp b/examples/datavisualization/texturesurface/topographicseries.cpp new file mode 100644 index 00000000..1d94aead --- /dev/null +++ b/examples/datavisualization/texturesurface/topographicseries.cpp @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +#include "topographicseries.h" + +#include + +using namespace QtDataVisualization; + +// Value used to encode height data as RGB value on PNG file +const float packingFactor = 11983.0f; + +TopographicSeries::TopographicSeries() +{ + setDrawMode(QSurface3DSeries::DrawSurface); + setFlatShadingEnabled(true); +} + +TopographicSeries::~TopographicSeries() +{ +} + +void TopographicSeries::setTopographyFile(const QString file, float width, float height) +{ + QImage heightMapImage(file); + uchar *bits = heightMapImage.bits(); + int imageHeight = heightMapImage.height(); + int imageWidth = heightMapImage.width(); + int widthBits = imageWidth * 4; + float stepX = width / float(imageWidth); + float stepZ = height / float(imageHeight); + + QSurfaceDataArray *dataArray = new QSurfaceDataArray; + dataArray->reserve(imageHeight); + for (int i = 0; i < imageHeight; i++) { + int p = (imageHeight - 1 - i) * widthBits; + QSurfaceDataRow *newRow = new QSurfaceDataRow(imageWidth); + for (int j = 0; j < imageWidth; j++) { + uchar aa = bits[p + 0]; + uchar rr = bits[p + 1]; + uchar gg = bits[p + 2]; + uint color = uint((gg << 16) + (rr << 8) + aa); + float y = float(color) / packingFactor; + (*newRow)[j].setPosition(QVector3D(float(j) * stepX, y, float(i) * stepZ)); + p = p + 4; + } + *dataArray << newRow; + } + + m_sampleCountX = float(imageWidth); + m_sampleCountZ = float(imageHeight); + + dataProxy()->resetArray(dataArray); +} diff --git a/examples/datavisualization/texturesurface/topographicseries.h b/examples/datavisualization/texturesurface/topographicseries.h new file mode 100644 index 00000000..06530c63 --- /dev/null +++ b/examples/datavisualization/texturesurface/topographicseries.h @@ -0,0 +1,45 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +#ifndef TOPOGRAPHICSERIES_H +#define TOPOGRAPHICSERIES_H + +#include + +using namespace QtDataVisualization; + +class TopographicSeries : public QSurface3DSeries +{ + Q_OBJECT +public: + explicit TopographicSeries(); + ~TopographicSeries(); + + void setTopographyFile(const QString file, float width, float height); + + float sampleCountX() { return m_sampleCountX; } + float sampleCountZ() { return m_sampleCountZ; } + +public slots: + +private: + float m_sampleCountX; + float m_sampleCountZ; +}; + +#endif // TOPOGRAPHICSERIES_H diff --git a/examples/datavisualization/texturesurface/topography.png b/examples/datavisualization/texturesurface/topography.png new file mode 100644 index 00000000..9349cdb3 Binary files /dev/null and b/examples/datavisualization/texturesurface/topography.png differ -- cgit v1.2.3 From 744609635867fdb673604b4128e62f7da6406632 Mon Sep 17 00:00:00 2001 From: Mika Salmela Date: Wed, 13 Aug 2014 14:28:57 +0300 Subject: Image for texture surface documentation Also theme and font size adjustment. Change-Id: I2306deceb8d45cd95fadb27e3b1ff88260f49ac9 Reviewed-by: Mika Salmela --- .../doc/images/texturesurface-example.png | Bin 0 -> 71994 bytes .../datavisualization/texturesurface/surfacegraph.cpp | 5 +++++ 2 files changed, 5 insertions(+) create mode 100644 examples/datavisualization/texturesurface/doc/images/texturesurface-example.png diff --git a/examples/datavisualization/texturesurface/doc/images/texturesurface-example.png b/examples/datavisualization/texturesurface/doc/images/texturesurface-example.png new file mode 100644 index 00000000..384552b4 Binary files /dev/null and b/examples/datavisualization/texturesurface/doc/images/texturesurface-example.png differ diff --git a/examples/datavisualization/texturesurface/surfacegraph.cpp b/examples/datavisualization/texturesurface/surfacegraph.cpp index b8e92988..6f62c6e1 100644 --- a/examples/datavisualization/texturesurface/surfacegraph.cpp +++ b/examples/datavisualization/texturesurface/surfacegraph.cpp @@ -44,6 +44,11 @@ SurfaceGraph::SurfaceGraph(Q3DSurface *surface) m_graph->axisX()->setLabelAutoRotation(30); m_graph->axisY()->setLabelAutoRotation(90); m_graph->axisZ()->setLabelAutoRotation(30); + m_graph->activeTheme()->setType(Q3DTheme::ThemeStoneMoss); + + QFont font = m_graph->activeTheme()->font(); + font.setPointSize(20); + m_graph->activeTheme()->setFont(font); m_topography = new TopographicSeries(); m_topography->setTopographyFile(":/maps/topography", areaWidth, areaHeight); -- cgit v1.2.3 From be7bdaa8930caf15fcc58a480d223e0c2b8af6ed Mon Sep 17 00:00:00 2001 From: Mika Salmela Date: Fri, 15 Aug 2014 08:55:20 +0300 Subject: Documentation for textured surface example MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I517b02a31e342dc3ae2c1c80805f811f9d9670ff Reviewed-by: Miikka Heikkinen Reviewed-by: Tomi Korpipää --- .../texturesurface/custominputhandler.cpp | 6 ++ .../texturesurface/doc/src/texturesurface.qdoc | 83 +++++++++++++++++++++- .../texturesurface/highlightseries.cpp | 4 ++ .../texturesurface/surfacegraph.cpp | 2 + .../texturesurface/topographicseries.cpp | 8 ++- 5 files changed, 99 insertions(+), 4 deletions(-) diff --git a/examples/datavisualization/texturesurface/custominputhandler.cpp b/examples/datavisualization/texturesurface/custominputhandler.cpp index 9072a0b4..0f79feef 100644 --- a/examples/datavisualization/texturesurface/custominputhandler.cpp +++ b/examples/datavisualization/texturesurface/custominputhandler.cpp @@ -46,6 +46,7 @@ void CustomInputHandler::mousePressEvent(QMouseEvent *event, const QPoint &mouse Q3DInputHandler::mousePressEvent(event, mousePos); } +//! [1] void CustomInputHandler::wheelEvent(QWheelEvent *event) { float delta = float(event->delta()); @@ -62,6 +63,7 @@ void CustomInputHandler::wheelEvent(QWheelEvent *event) m_axisY->setRange(100.0f, y); m_axisZ->setRange(m_axisZMinValue, m_axisZMaxValue); } +//! [1] void CustomInputHandler::mouseMoveEvent(QMouseEvent *event, const QPoint &mousePos) { @@ -115,6 +117,7 @@ void CustomInputHandler::handleAxisDragging() // Adjust axes switch (m_state) { +//! [0] case StateDraggingX: distance = (move.x() * xMulX - move.y() * xMulY) * m_speedModifier; m_axisXMinValue -= distance; @@ -131,6 +134,7 @@ void CustomInputHandler::handleAxisDragging() } m_axisX->setRange(m_axisXMinValue, m_axisXMaxValue); break; +//! [0] case StateDraggingZ: distance = (move.x() * zMulX + move.y() * zMulY) * m_speedModifier; m_axisZMinValue += distance; @@ -154,6 +158,7 @@ void CustomInputHandler::handleAxisDragging() void CustomInputHandler::checkConstraints() { +//! [2] if (m_axisXMinValue < m_areaMinValue) m_axisXMinValue = m_areaMinValue; if (m_axisXMaxValue > m_areaMaxValue) @@ -164,6 +169,7 @@ void CustomInputHandler::checkConstraints() m_axisXMinValue -= adjust; m_axisXMaxValue += adjust; } +//! [2] if (m_axisZMinValue < m_areaMinValue) m_axisZMinValue = m_areaMinValue; diff --git a/examples/datavisualization/texturesurface/doc/src/texturesurface.qdoc b/examples/datavisualization/texturesurface/doc/src/texturesurface.qdoc index ee93dd0a..a4aa219e 100644 --- a/examples/datavisualization/texturesurface/doc/src/texturesurface.qdoc +++ b/examples/datavisualization/texturesurface/doc/src/texturesurface.qdoc @@ -18,9 +18,88 @@ /*! \example texturesurface - \title Texture Surface Example + \title Textured Surface Example \ingroup qtdatavisualization_examples \brief Using texture with Q3DSurface. - TODO + The textured surface example shows how to add an image as a texture for a surface. The example + shows also how to: + + \list + \li Create a surface series from an image + \li Highlight an area of the surface + \li Use custom input handler to enable zooming and panning + \endlist + + \image texturesurface-example.png + + \section1 Texture to a surface series + + The image to be set as a texture to a surface can be set using QSurface3DSeries::setTextureFile(). + In this example we have added a check box to control if the texture is set or not. The + following code extract is for reacting to the check box selections. The image in this + example is read from the resource file where it is as a JPG file. Setting an empty file + with the method clears the texture, and the surface uses the gradients or colors from the theme. + + \snippet texturesurface/surfacegraph.cpp 0 + + \section1 Topographic surface series + + The topographic data for this example is obtained from National Land Survey of Finland. It + provides a product called \c{Elevation Model 2 m}, which was suitable for our needs. We selected + Levi fell to be shown. The accuracy of the data was well beyond our needs and therefore it + is compressed and encoded into a PNG file. The height value from the original ASCII data is + encoded into RGB format using a multiplier, which you will see later on a code extract. + The multiplier is calculated simply by dividing the largest 24 bit value with the highest point + in Finland. + + Qt Data Visualization has a special proxy for height map image files, but it converts + only one byte values. So to utilize the bigger accuracy on the data from National Land + Survey of Finland, we read the data from the PNG file and decode it into QSurface3DSeries. + The following code samples show how this is done. + + First the encoding multiplier. + \snippet texturesurface/topographicseries.cpp 0 + + And then the actual decoding. + \snippet texturesurface/topographicseries.cpp 1 + + \section1 Highlight an area of the surface + + The main idea on creating a highlight on the surface is to create a copy of the series and add + a bit of offset to the y value. On this example the class \c HighlightSeries implements the + creation of the copy on its \c handlePositionChange method. Firstly the \c HighlightSeries + needs to get the pointer to the original series and then it starts to listen the + QSurface3DSeries::selectedPointChanged signal. + + \snippet texturesurface/highlightseries.cpp 0 + + When the signal arrives, first thing is to check that the position is valid. Then the ranges + for the copied area are calculated and checked that they stay within the bounds. Finally + we simply fill the data array of the highlight series with the range from the data array of + topography series. + + \snippet texturesurface/highlightseries.cpp 1 + + \section1 Use custom input handler to enable zooming and panning + + For the panning the implementation is similar to the \l{Axis Range Dragging With Labels Example}. + The difference is that in this example we follow only dragging of X and Z axis and we don't + allow dragging the surface outside the graph. The control for this is very simple and done as + on the following example for the X axis. + + \snippet texturesurface/custominputhandler.cpp 0 + + For the zooming we catch the \c wheelEvent and adjust the X and Y axis ranges according to delta + value on QWheelEvent. The Y axis is also adjusted so that the aspect ratio between Y axis and + XZ plane stays the same, and we don't get silly looking graph with height exaggerated too much. + + \snippet texturesurface/custominputhandler.cpp 1 + + In this case we want to control the zoom level so that it won't get too near to or far from the + surface. For instance, if the value for the X axis gets below the allowed, i.e. zooming gets too + far, the value is set to the minimum allowed value. If the range is going to below the range + minimum, both ends of the axis are adjusted so that the range stays at the limit. + + \snippet texturesurface/custominputhandler.cpp 2 */ diff --git a/examples/datavisualization/texturesurface/highlightseries.cpp b/examples/datavisualization/texturesurface/highlightseries.cpp index 3584d1d4..7e5f7efb 100644 --- a/examples/datavisualization/texturesurface/highlightseries.cpp +++ b/examples/datavisualization/texturesurface/highlightseries.cpp @@ -35,6 +35,7 @@ HighlightSeries::~HighlightSeries() { } +//! [0] void HighlightSeries::setTopographicSeries(TopographicSeries *series) { m_topographicSeries = series; @@ -44,7 +45,9 @@ void HighlightSeries::setTopographicSeries(TopographicSeries *series) QObject::connect(m_topographicSeries, &QSurface3DSeries::selectedPointChanged, this, &HighlightSeries::handlePositionChange); } +//! [0] +//! [1] void HighlightSeries::handlePositionChange(const QPoint &position) { m_position = position; @@ -89,3 +92,4 @@ void HighlightSeries::handlePositionChange(const QPoint &position) dataProxy()->resetArray(dataArray); setVisible(true); } +//! [1] diff --git a/examples/datavisualization/texturesurface/surfacegraph.cpp b/examples/datavisualization/texturesurface/surfacegraph.cpp index 6f62c6e1..f85ef5c8 100644 --- a/examples/datavisualization/texturesurface/surfacegraph.cpp +++ b/examples/datavisualization/texturesurface/surfacegraph.cpp @@ -74,6 +74,7 @@ SurfaceGraph::~SurfaceGraph() delete m_graph; } +//! [0] void SurfaceGraph::toggleSurfaceTexture(bool enable) { if (enable) @@ -81,3 +82,4 @@ void SurfaceGraph::toggleSurfaceTexture(bool enable) else m_topography->setTextureFile(""); } +//! [0] diff --git a/examples/datavisualization/texturesurface/topographicseries.cpp b/examples/datavisualization/texturesurface/topographicseries.cpp index 1d94aead..a1c61f56 100644 --- a/examples/datavisualization/texturesurface/topographicseries.cpp +++ b/examples/datavisualization/texturesurface/topographicseries.cpp @@ -22,8 +22,10 @@ using namespace QtDataVisualization; +//! [0] // Value used to encode height data as RGB value on PNG file const float packingFactor = 11983.0f; +//! [0] TopographicSeries::TopographicSeries() { @@ -37,6 +39,7 @@ TopographicSeries::~TopographicSeries() void TopographicSeries::setTopographyFile(const QString file, float width, float height) { +//! [1] QImage heightMapImage(file); uchar *bits = heightMapImage.bits(); int imageHeight = heightMapImage.height(); @@ -62,8 +65,9 @@ void TopographicSeries::setTopographyFile(const QString file, float width, float *dataArray << newRow; } + dataProxy()->resetArray(dataArray); +//! [1] + m_sampleCountX = float(imageWidth); m_sampleCountZ = float(imageHeight); - - dataProxy()->resetArray(dataArray); } -- cgit v1.2.3 From 169a4d638c6c1b6634ffcfd19c4fe3cb94cf27d5 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Wed, 13 Aug 2014 15:09:36 +0300 Subject: Implement volume rendering support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit New subclass of QCustom3DItem, QCustom3DVolume is provided. The documentation for the example will be done in a separate commit. Change-Id: Idb3fdb0654c6bec7606ca012b75852a5a8412397 Reviewed-by: Tomi Korpipää --- examples/datavisualization/datavisualization.pro | 3 +- .../volumetric/doc/src/volumetric.qdoc | 27 + examples/datavisualization/volumetric/main.cpp | 105 ++++ .../datavisualization/volumetric/volumetric.cpp | 179 ++++++ examples/datavisualization/volumetric/volumetric.h | 59 ++ .../datavisualization/volumetric/volumetric.pro | 15 + src/datavisualization/data/customrenderitem.cpp | 25 +- src/datavisualization/data/customrenderitem_p.h | 35 ++ src/datavisualization/data/data.pri | 7 +- src/datavisualization/data/qcustom3ditem.cpp | 6 +- src/datavisualization/data/qcustom3ditem_p.h | 1 + src/datavisualization/data/qcustom3dvolume.cpp | 669 +++++++++++++++++++++ src/datavisualization/data/qcustom3dvolume.h | 106 ++++ src/datavisualization/data/qcustom3dvolume_p.h | 90 +++ .../engine/abstract3drenderer.cpp | 176 +++++- .../engine/abstract3drenderer_p.h | 11 +- src/datavisualization/engine/bars3drenderer.cpp | 15 +- src/datavisualization/engine/drawer.cpp | 19 +- src/datavisualization/engine/drawer_p.h | 2 +- src/datavisualization/engine/engine.qrc | 3 + src/datavisualization/engine/scatter3drenderer.cpp | 19 +- src/datavisualization/engine/shaders/default.frag | 1 + .../engine/shaders/texture3d.frag | 70 +++ .../engine/shaders/texture3d.vert | 12 + .../engine/shaders/texture3dslice.frag | 119 ++++ src/datavisualization/engine/surface3drenderer.cpp | 23 +- src/datavisualization/utils/shaderhelper.cpp | 37 ++ src/datavisualization/utils/shaderhelper_p.h | 9 + src/datavisualization/utils/texturehelper.cpp | 60 ++ src/datavisualization/utils/texturehelper_p.h | 11 + tests/tests.pro | 3 +- tests/volumetrictest/logo.png | Bin 0 -> 2205 bytes tests/volumetrictest/main.cpp | 105 ++++ tests/volumetrictest/volumetrictest.cpp | 282 +++++++++ tests/volumetrictest/volumetrictest.h | 61 ++ tests/volumetrictest/volumetrictest.pro | 18 + tests/volumetrictest/volumetrictest.qrc | 5 + 37 files changed, 2343 insertions(+), 45 deletions(-) create mode 100644 examples/datavisualization/volumetric/doc/src/volumetric.qdoc create mode 100644 examples/datavisualization/volumetric/main.cpp create mode 100644 examples/datavisualization/volumetric/volumetric.cpp create mode 100644 examples/datavisualization/volumetric/volumetric.h create mode 100644 examples/datavisualization/volumetric/volumetric.pro create mode 100644 src/datavisualization/data/qcustom3dvolume.cpp create mode 100644 src/datavisualization/data/qcustom3dvolume.h create mode 100644 src/datavisualization/data/qcustom3dvolume_p.h create mode 100644 src/datavisualization/engine/shaders/texture3d.frag create mode 100644 src/datavisualization/engine/shaders/texture3d.vert create mode 100644 src/datavisualization/engine/shaders/texture3dslice.frag create mode 100644 tests/volumetrictest/logo.png create mode 100644 tests/volumetrictest/main.cpp create mode 100644 tests/volumetrictest/volumetrictest.cpp create mode 100644 tests/volumetrictest/volumetrictest.h create mode 100644 tests/volumetrictest/volumetrictest.pro create mode 100644 tests/volumetrictest/volumetrictest.qrc diff --git a/examples/datavisualization/datavisualization.pro b/examples/datavisualization/datavisualization.pro index cb861b81..eab15b6d 100644 --- a/examples/datavisualization/datavisualization.pro +++ b/examples/datavisualization/datavisualization.pro @@ -21,7 +21,8 @@ SUBDIRS += qmlbars \ rotations \ draggableaxes \ customitems \ - texturesurface + texturesurface \ + volumetric } qtHaveModule(multimedia):!android:!ios: SUBDIRS += audiolevels diff --git a/examples/datavisualization/volumetric/doc/src/volumetric.qdoc b/examples/datavisualization/volumetric/doc/src/volumetric.qdoc new file mode 100644 index 00000000..48651cb1 --- /dev/null +++ b/examples/datavisualization/volumetric/doc/src/volumetric.qdoc @@ -0,0 +1,27 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +/*! + \example volumetric + \title Volumetric rendering Example + \ingroup qtdatavisualization_examples + \brief Rendering volumetric objects. + + TODO + \section1 Example contents +*/ diff --git a/examples/datavisualization/volumetric/main.cpp b/examples/datavisualization/volumetric/main.cpp new file mode 100644 index 00000000..7e4c9973 --- /dev/null +++ b/examples/datavisualization/volumetric/main.cpp @@ -0,0 +1,105 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +#include "volumetric.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int main(int argc, char **argv) +{ + QApplication app(argc, argv); + Q3DScatter *graph = new Q3DScatter(); + QWidget *container = QWidget::createWindowContainer(graph); + + QSize screenSize = graph->screen()->size(); + container->setMinimumSize(QSize(screenSize.width() / 2, screenSize.height() / 1.5)); + container->setMaximumSize(screenSize); + container->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + container->setFocusPolicy(Qt::StrongFocus); + + QWidget *widget = new QWidget; + QHBoxLayout *hLayout = new QHBoxLayout(widget); + QVBoxLayout *vLayout = new QVBoxLayout(); + hLayout->addWidget(container, 1); + hLayout->addLayout(vLayout); + + widget->setWindowTitle(QStringLiteral("Volumetric Object Example")); + + QCheckBox *sliceXCheckBox = new QCheckBox(widget); + sliceXCheckBox->setText(QStringLiteral("Slice volume on X axis")); + sliceXCheckBox->setChecked(false); + QCheckBox *sliceYCheckBox = new QCheckBox(widget); + sliceYCheckBox->setText(QStringLiteral("Slice volume on Y axis")); + sliceYCheckBox->setChecked(false); + QCheckBox *sliceZCheckBox = new QCheckBox(widget); + sliceZCheckBox->setText(QStringLiteral("Slice volume on Z axis")); + sliceZCheckBox->setChecked(false); + + QSlider *sliceXSlider = new QSlider(Qt::Horizontal, widget); + sliceXSlider->setMinimum(0); + sliceXSlider->setMaximum(1024); + sliceXSlider->setValue(512); + sliceXSlider->setEnabled(true); + QSlider *sliceYSlider = new QSlider(Qt::Horizontal, widget); + sliceYSlider->setMinimum(0); + sliceYSlider->setMaximum(1024); + sliceYSlider->setValue(512); + sliceYSlider->setEnabled(true); + QSlider *sliceZSlider = new QSlider(Qt::Horizontal, widget); + sliceZSlider->setMinimum(0); + sliceZSlider->setMaximum(1024); + sliceZSlider->setValue(512); + sliceZSlider->setEnabled(true); + + QLabel *fpsLabel = new QLabel(QStringLiteral("Fps: "), widget); + + vLayout->addWidget(fpsLabel); + vLayout->addWidget(sliceXCheckBox); + vLayout->addWidget(sliceXSlider); + vLayout->addWidget(sliceYCheckBox); + vLayout->addWidget(sliceYSlider); + vLayout->addWidget(sliceZCheckBox); + vLayout->addWidget(sliceZSlider, 1, Qt::AlignTop); + + VolumetricModifier *modifier = new VolumetricModifier(graph); + modifier->setFpsLabel(fpsLabel); + + QObject::connect(sliceXCheckBox, &QCheckBox::stateChanged, modifier, + &VolumetricModifier::sliceX); + QObject::connect(sliceYCheckBox, &QCheckBox::stateChanged, modifier, + &VolumetricModifier::sliceY); + QObject::connect(sliceZCheckBox, &QCheckBox::stateChanged, modifier, + &VolumetricModifier::sliceZ); + QObject::connect(sliceXSlider, &QSlider::valueChanged, modifier, + &VolumetricModifier::adjustSliceX); + QObject::connect(sliceYSlider, &QSlider::valueChanged, modifier, + &VolumetricModifier::adjustSliceY); + QObject::connect(sliceZSlider, &QSlider::valueChanged, modifier, + &VolumetricModifier::adjustSliceZ); + + widget->show(); + return app.exec(); +} diff --git a/examples/datavisualization/volumetric/volumetric.cpp b/examples/datavisualization/volumetric/volumetric.cpp new file mode 100644 index 00000000..a9233cab --- /dev/null +++ b/examples/datavisualization/volumetric/volumetric.cpp @@ -0,0 +1,179 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +#include "volumetric.h" +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace QtDataVisualization; + +const int textureSize = 256; + +VolumetricModifier::VolumetricModifier(Q3DScatter *scatter) + : m_graph(scatter), + m_volumeItem(0), + m_sliceIndexX(textureSize / 2), + m_sliceIndexY(textureSize / 4), + m_sliceIndexZ(textureSize / 2) +{ + m_graph->activeTheme()->setType(Q3DTheme::ThemeQt); + m_graph->setShadowQuality(QAbstract3DGraph::ShadowQualityNone); + m_graph->scene()->activeCamera()->setCameraPreset(Q3DCamera::CameraPresetFront); + m_graph->setOrthoProjection(true); + + createVolume(); + + m_graph->addCustomItem(m_volumeItem); + m_graph->setMeasureFps(true); + + QObject::connect(m_graph->scene()->activeCamera(), &Q3DCamera::zoomLevelChanged, this, + &VolumetricModifier::handleZoomLevelChange); + QObject::connect(m_graph, &QAbstract3DGraph::currentFpsChanged, this, + &VolumetricModifier::handleFpsChange); +} + +VolumetricModifier::~VolumetricModifier() +{ + delete m_graph; +} + +void VolumetricModifier::setFpsLabel(QLabel *fpsLabel) +{ + m_fpsLabel = fpsLabel; +} + +void VolumetricModifier::sliceX(int enabled) +{ + m_volumeItem->setSliceIndexX(enabled ? m_sliceIndexX : -1); +} + +void VolumetricModifier::sliceY(int enabled) +{ + m_volumeItem->setSliceIndexY(enabled ? m_sliceIndexY : -1); +} + +void VolumetricModifier::sliceZ(int enabled) +{ + m_volumeItem->setSliceIndexZ(enabled ? m_sliceIndexZ : -1); +} + +void VolumetricModifier::adjustSliceX(int value) +{ + m_sliceIndexX = value / (1024 / textureSize); + if (m_volumeItem->sliceIndexX() != -1) + m_volumeItem->setSliceIndexX(m_sliceIndexX); +} + +void VolumetricModifier::adjustSliceY(int value) +{ + m_sliceIndexY = value / (1024 / textureSize * 2); + if (m_volumeItem->sliceIndexY() != -1) + m_volumeItem->setSliceIndexY(m_sliceIndexY); +} + +void VolumetricModifier::adjustSliceZ(int value) +{ + m_sliceIndexZ = value / (1024 / textureSize); + if (m_volumeItem->sliceIndexZ() != -1) + m_volumeItem->setSliceIndexZ(m_sliceIndexZ); +} + +void VolumetricModifier::handleZoomLevelChange() +{ + // Zooming inside volumetric object causes ugly clipping issues, so restrict zoom level a bit + if (m_graph->scene()->activeCamera()->zoomLevel() > 220) + m_graph->scene()->activeCamera()->setZoomLevel(220); +} + +void VolumetricModifier::handleFpsChange(qreal fps) +{ + const QString fpsFormat = QStringLiteral("Fps: %1"); + int fps10 = int(fps * 10.0); + m_fpsLabel->setText(fpsFormat.arg(QString::number(qreal(fps10) / 10.0))); +} + +void VolumetricModifier::createVolume() +{ + m_volumeItem = new QCustom3DVolume; + m_volumeItem->setScaling(QVector3D(2.0f, 1.0f, 2.0f)); + m_volumeItem->setTextureWidth(textureSize); + m_volumeItem->setTextureHeight(textureSize / 2); + m_volumeItem->setTextureDepth(textureSize); + m_volumeItem->setTextureFormat(QImage::Format_Indexed8); + + // Generate color table. First color is fully transparent used to fill outer + // portions of the volume. The second, red layer, is somewhat transparent. + // Rest of to colors are opaque. + QVector colors; + colors.resize(256); + + colors[0] = qRgba(0, 0, 0, 0); + for (int i = 1; i < 256; i++) { + if (i < 60) { + colors[i] = qRgba((i * 2) + 120, 0, 0, 100); + } else if (i < 120) { + colors[i] = qRgba(0, ((i - 60) * 2) + 120, 0, 255); + } else if (i < 180) { + colors[i] = qRgba(0, 0, ((i - 120) * 2) + 120, 255); + } else { + colors[i] = qRgba(i, i, i, 255); + } + } + m_volumeItem->setColorTable(colors); + + // Generate texture data for an half-ellipsoid. + // This can take a while if the dimensions are large. + // Note that in real world cases, the texture data is usually be supplied + // as a stack of slice images. + + QVector *textureData = new QVector(textureSize * textureSize * textureSize / 2); + + QVector3D midPoint(float(textureSize) / 2.0f, + float(textureSize) / 2.0f, + float(textureSize) / 2.0f); + + int index = 0; + for (int i = 0; i < textureSize; i++) { + for (int j = 0; j < textureSize / 2; j++) { + for (int k = 0; k < textureSize; k++) { + int colorIndex = 0; + // Take a slice out of the ellipsoid + if (i >= textureSize / 2 || j >= textureSize / 4 || k >= textureSize / 2) { + QVector3D distVec = QVector3D(float(k), float(j * 2), float(i)) - midPoint; + float adjLen = qMin(255.0f, (distVec.length() * float(textureSize / 128))); + if (adjLen < 230) + colorIndex = 255 - int(adjLen); + else + colorIndex = 0; + } + + (*textureData)[index] = colorIndex; + index++; + } + } + } + + m_volumeItem->setTextureData(textureData); +} + diff --git a/examples/datavisualization/volumetric/volumetric.h b/examples/datavisualization/volumetric/volumetric.h new file mode 100644 index 00000000..722d4b48 --- /dev/null +++ b/examples/datavisualization/volumetric/volumetric.h @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +#ifndef VOLUMETRICMODIFIER_H +#define VOLUMETRICMODIFIER_H + +#include +#include + +class QLabel; + +using namespace QtDataVisualization; + +class VolumetricModifier : public QObject +{ + Q_OBJECT +public: + explicit VolumetricModifier(Q3DScatter *scatter); + ~VolumetricModifier(); + + void setFpsLabel(QLabel *fpsLabel); + +public slots: + void sliceX(int enabled); + void sliceY(int enabled); + void sliceZ(int enabled); + void adjustSliceX(int value); + void adjustSliceY(int value); + void adjustSliceZ(int value); + void handleZoomLevelChange(); + void handleFpsChange(qreal fps); + +private: + void createVolume(); + + Q3DScatter *m_graph; + QCustom3DVolume *m_volumeItem; + int m_sliceIndexX; + int m_sliceIndexY; + int m_sliceIndexZ; + QLabel *m_fpsLabel; +}; + +#endif diff --git a/examples/datavisualization/volumetric/volumetric.pro b/examples/datavisualization/volumetric/volumetric.pro new file mode 100644 index 00000000..c5e31891 --- /dev/null +++ b/examples/datavisualization/volumetric/volumetric.pro @@ -0,0 +1,15 @@ +android|ios { + error( "This example is not supported for android or ios." ) +} + +!include( ../examples.pri ) { + error( "Couldn't find the examples.pri file!" ) +} + +SOURCES += main.cpp volumetric.cpp +HEADERS += volumetric.h + +QT += widgets + +OTHER_FILES += doc/src/* \ + doc/images/* diff --git a/src/datavisualization/data/customrenderitem.cpp b/src/datavisualization/data/customrenderitem.cpp index a1c70057..b96a4957 100644 --- a/src/datavisualization/data/customrenderitem.cpp +++ b/src/datavisualization/data/customrenderitem.cpp @@ -23,9 +23,20 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION CustomRenderItem::CustomRenderItem() : AbstractRenderItem(), m_texture(0), + m_absolute(false), m_object(0), + m_needBlend(true), m_visible(true), - m_renderer(0) + m_valid(true), + m_index(0), + m_shadowCasting(false), + m_isFacingCamera(false), + m_item(0), + m_renderer(0), + m_textureWidth(0), + m_textureHeight(0), + m_textureDepth(0), + m_isVolume(false) { } @@ -39,4 +50,16 @@ void CustomRenderItem::setMesh(const QString &meshFile) ObjectHelper::resetObjectHelper(m_renderer, m_object, meshFile); } +void CustomRenderItem::setColorTable(const QVector &colors) +{ + m_colorTable.resize(colors.size()); + for (int i = 0; i < m_colorTable.size(); i++) { + const QRgb &rgb = colors.at(i); + m_colorTable[i] = QVector4D(float(qRed(rgb)) / 255.0f, + float(qGreen(rgb)) / 255.0f, + float(qBlue(rgb)) / 255.0f, + float(qAlpha(rgb)) / 255.0f); + } +} + QT_END_NAMESPACE_DATAVISUALIZATION diff --git a/src/datavisualization/data/customrenderitem_p.h b/src/datavisualization/data/customrenderitem_p.h index 4fb94276..e21b6f39 100644 --- a/src/datavisualization/data/customrenderitem_p.h +++ b/src/datavisualization/data/customrenderitem_p.h @@ -31,6 +31,8 @@ #include "abstractrenderitem_p.h" #include "objecthelper_p.h" +#include +#include QT_BEGIN_NAMESPACE_DATAVISUALIZATION @@ -69,6 +71,28 @@ public: inline bool isFacingCamera() const { return m_isFacingCamera; } inline void setRenderer(Abstract3DRenderer *renderer) { m_renderer = renderer; } + // Volume specific + inline void setTextureWidth(int width) { m_textureWidth = width; } + inline int textureWidth() const { return m_textureWidth; } + inline void setTextureHeight(int height) { m_textureHeight = height; } + inline int textureHeight() const { return m_textureHeight; } + inline void setTextureDepth(int depth) { m_textureDepth = depth; } + inline int textureDepth() const { return m_textureDepth; } + inline int textureSize() const { return m_textureWidth * m_textureHeight * m_textureDepth; } + inline void setColorTable(const QVector &colors) { m_colorTable = colors; } + void setColorTable(const QVector &colors); + inline const QVector &colorTable() const { return m_colorTable; } + inline void setVolume(bool volume) { m_isVolume = volume; } + inline bool isVolume() const { return m_isVolume; } + inline void setTextureFormat(QImage::Format format) { m_textureFormat = format; } + inline QImage::Format textureFormat() const { return m_textureFormat; } + inline void setSliceIndexX(int index) { m_sliceIndexX = index; } + inline void setSliceIndexY(int index) { m_sliceIndexY = index; } + inline void setSliceIndexZ(int index) { m_sliceIndexZ = index; } + int sliceIndexX() const { return m_sliceIndexX; } + int sliceIndexY() const { return m_sliceIndexY; } + int sliceIndexZ() const { return m_sliceIndexZ; } + private: Q_DISABLE_COPY(CustomRenderItem) @@ -85,6 +109,17 @@ private: bool m_isFacingCamera; QCustom3DItem *m_item; Abstract3DRenderer *m_renderer; + + // Volume specific + int m_textureWidth; + int m_textureHeight; + int m_textureDepth; + QVector m_colorTable; + bool m_isVolume; + QImage::Format m_textureFormat; + int m_sliceIndexX; + int m_sliceIndexY; + int m_sliceIndexZ; }; typedef QHash CustomRenderItemArray; diff --git a/src/datavisualization/data/data.pri b/src/datavisualization/data/data.pri index 37a8d084..e2bbd6eb 100644 --- a/src/datavisualization/data/data.pri +++ b/src/datavisualization/data/data.pri @@ -41,7 +41,9 @@ HEADERS += \ $$PWD/qcustom3ditem.h \ $$PWD/qcustom3ditem_p.h \ $$PWD/qcustom3dlabel.h \ - $$PWD/qcustom3dlabel_p.h + $$PWD/qcustom3dlabel_p.h \ + $$PWD/qcustom3dvolume.h \ + $$PWD/qcustom3dvolume_p.h SOURCES += \ $$PWD/labelitem.cpp \ @@ -69,6 +71,7 @@ SOURCES += \ $$PWD/qsurface3dseries.cpp \ $$PWD/customrenderitem.cpp \ $$PWD/qcustom3ditem.cpp \ - $$PWD/qcustom3dlabel.cpp + $$PWD/qcustom3dlabel.cpp \ + $$PWD/qcustom3dvolume.cpp INCLUDEPATH += $$PWD diff --git a/src/datavisualization/data/qcustom3ditem.cpp b/src/datavisualization/data/qcustom3ditem.cpp index efee3b15..1d419cee 100644 --- a/src/datavisualization/data/qcustom3ditem.cpp +++ b/src/datavisualization/data/qcustom3ditem.cpp @@ -373,7 +373,8 @@ QCustom3DItemPrivate::QCustom3DItemPrivate(QCustom3DItem *q) : m_rotation(QQuaternion(0.0f, 0.0f, 0.0f, 0.0f)), m_visible(true), m_shadowCasting(true), - m_isLabelItem(false) + m_isLabelItem(false), + m_isVolumeItem(false) { } @@ -388,7 +389,8 @@ QCustom3DItemPrivate::QCustom3DItemPrivate(QCustom3DItem *q, const QString &mesh m_rotation(rotation), m_visible(true), m_shadowCasting(true), - m_isLabelItem(false) + m_isLabelItem(false), + m_isVolumeItem(false) { } diff --git a/src/datavisualization/data/qcustom3ditem_p.h b/src/datavisualization/data/qcustom3ditem_p.h index c1ce5996..d766bcf3 100644 --- a/src/datavisualization/data/qcustom3ditem_p.h +++ b/src/datavisualization/data/qcustom3ditem_p.h @@ -82,6 +82,7 @@ public: bool m_shadowCasting; bool m_isLabelItem; + bool m_isVolumeItem; QCustomItemDirtyBitField m_dirtyBits; diff --git a/src/datavisualization/data/qcustom3dvolume.cpp b/src/datavisualization/data/qcustom3dvolume.cpp new file mode 100644 index 00000000..766f1589 --- /dev/null +++ b/src/datavisualization/data/qcustom3dvolume.cpp @@ -0,0 +1,669 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +#include "qcustom3dvolume_p.h" +#include "utils_p.h" + +QT_BEGIN_NAMESPACE_DATAVISUALIZATION + +/*! + * \class QCustom3DVolume + * \inmodule QtDataVisualization + * \brief The QCustom3DVolume class is for creating volume rendered objects to be added to a graph. + * \since QtDataVisualization 1.2 + * + * This class is for creating volume rendered objects to be added to a graph. A volume rendered + * object is a box with a 3D texture. Three slice planes are supported for the volume, one along + * each main axis of the volume. + * + * \note Volumetric objects are only supported with orthographic projection. + * + * \sa QAbstract3DGraph::addCustomItem(), QAbstract3DGraph::orthoProjection + */ + +/*! + * \qmltype Custom3DVolume + * \inqmlmodule QtDataVisualization + * \since QtDataVisualization 1.2 + * \ingroup datavisualization_qml + * \instantiates QCustom3DVolume + * \brief The Custom3DVolume type is for creating volume rendered objects to be added to a graph. + * + * This class is for creating volume rendered objects to be added to a graph. A volume rendered + * object is a box with a 3D texture. Three slice planes are supported for the volume, one along + * each main axis of the volume. + * + * \note: Filling in the volume data would not typically be efficient or practical from pure QML, + * so properties directly related to that are not fully supported from QML. + * Make a hybrid QML/C++ application if you want to use volume objects with QML ui. + * + * \note Volumetric objects are only supported with orthographic projection. + * + * \sa AbstractGraph3D::orthoProjection + */ + +/*! \qmlproperty int Custom3DVolume::textureWidth + * + * The width of the 3D texture defining the volume content in pixels. Defaults to \c{0}. + * + * \note: Changing this property from QML is not supported, as the texture data cannot be resized + * to match. + */ + +/*! \qmlproperty int Custom3DVolume::textureHeight + * + * The height of the 3D texture defining the volume content in pixels. Defaults to \c{0}. + * + * \note: Changing this property from QML is not supported, as the texture data cannot be resized + * to match. + */ + +/*! \qmlproperty int Custom3DVolume::textureDepth + * + * The depth of the 3D texture defining the volume content in pixels. Defaults to \c{0}. + * + * \note: Changing this property from QML is not supported, as the texture data cannot be resized + * to match. + */ + +/*! \qmlproperty int Custom3DVolume::sliceIndexX + * + * The X dimension index into the texture data indicating which vertical slice to show. + * Setting any dimension to negative indicates no slice for that dimension is drawn. + * If all dimensions are negative, no slices are drawn and the volume is drawn normally. + * Defaults to \c{-1}. + * + * \sa QCustom3DVolume::textureData + */ + +/*! \qmlproperty int Custom3DVolume::sliceIndexY + * + * The Y dimension index into the texture data indicating which horizontal slice to show. + * Setting any dimension to negative indicates no slice for that dimension is drawn. + * If all dimensions are negative, no slices are drawn and the volume is drawn normally. + * Defaults to \c{-1}. + * + * \sa QCustom3DVolume::textureData + */ + +/*! \qmlproperty int Custom3DVolume::sliceIndexZ + * + * The Z dimension index into the texture data indicating which vertical slice to show. + * Setting any dimension to negative indicates no slice for that dimension is drawn. + * If all dimensions are negative, no slices are drawn and the volume is drawn normally. + * Defaults to \c{-1}. + * + * \sa QCustom3DVolume::textureData + */ + +/*! + * Constructs QCustom3DVolume with given \a parent. + */ +QCustom3DVolume::QCustom3DVolume(QObject *parent) : + QCustom3DItem(new QCustom3DVolumePrivate(this), parent) +{ +} + +/*! + * Constructs QCustom3DVolume with given \a position, \a scaling, \a rotation, + * \a textureWidth, \a textureHeight, \a textureDepth, \a textureData, \a textureFormat, + * \a colorTable, and optional \a parent. + * + * \sa textureData, textureFormat, colorTable + */ +QCustom3DVolume::QCustom3DVolume(const QVector3D &position, const QVector3D &scaling, + const QQuaternion &rotation, int textureWidth, + int textureHeight, int textureDepth, + QVector *textureData, QImage::Format textureFormat, + const QVector &colorTable, QObject *parent) : + QCustom3DItem(new QCustom3DVolumePrivate(this, position, scaling, rotation, textureWidth, + textureHeight, textureDepth, + textureData, textureFormat, colorTable), parent) +{ +} + + +/*! + * Destroys QCustom3DVolume. + */ +QCustom3DVolume::~QCustom3DVolume() +{ +} + +/*! \property QCustom3DVolume::textureWidth + * + * The width of the 3D texture defining the volume content in pixels. Defaults to \c{0}. + * + * \note The textureData may need to be resized or recreated if this value is changed. + * Defaults to \c{0}. + * + * \sa textureData, textureHeight, textureDepth, textureFormat, textureDataWidth() + */ +void QCustom3DVolume::setTextureWidth(int value) +{ + if (value >= 0) { + if (dptr()->m_textureWidth != value) { + dptr()->m_textureWidth = value; + dptr()->m_dirtyBitsVolume.textureDimensionsDirty = true; + emit textureWidthChanged(value); + emit dptr()->needUpdate(); + } + } else { + qWarning() << __FUNCTION__ << "Cannot set negative value."; + } +} + +int QCustom3DVolume::textureWidth() const +{ + return dptrc()->m_textureWidth; +} + +/*! \property QCustom3DVolume::textureHeight + * + * The height of the 3D texture defining the volume content in pixels. Defaults to \c{0}. + * + * \note The textureData may need to be resized or recreated if this value is changed. + * Defaults to \c{0}. + * + * \sa textureData, textureWidth, textureDepth, textureFormat + */ +void QCustom3DVolume::setTextureHeight(int value) +{ + if (value >= 0) { + if (dptr()->m_textureHeight != value) { + dptr()->m_textureHeight = value; + dptr()->m_dirtyBitsVolume.textureDimensionsDirty = true; + emit textureHeightChanged(value); + emit dptr()->needUpdate(); + } + } else { + qWarning() << __FUNCTION__ << "Cannot set negative value."; + } + +} + +int QCustom3DVolume::textureHeight() const +{ + return dptrc()->m_textureHeight; +} + +/*! \property QCustom3DVolume::textureDepth + * + * The depth of the 3D texture defining the volume content in pixels. Defaults to \c{0}. + * + * \note The textureData may need to be resized or recreated if this value is changed. + * Defaults to \c{0}. + * + * \sa textureData, textureWidth, textureHeight, textureFormat + */ +void QCustom3DVolume::setTextureDepth(int value) +{ + if (value >= 0) { + if (dptr()->m_textureDepth != value) { + dptr()->m_textureDepth = value; + dptr()->m_dirtyBitsVolume.textureDimensionsDirty = true; + emit textureDepthChanged(value); + emit dptr()->needUpdate(); + } + } else { + qWarning() << __FUNCTION__ << "Cannot set negative value."; + } +} + +int QCustom3DVolume::textureDepth() const +{ + return dptrc()->m_textureDepth; +} + +/*! + * A convenience function for setting all three texture dimensions + * (\a width, \a height, and \a depth) at once. + * + * \sa textureData + */ +void QCustom3DVolume::setTextureDimensions(int width, int height, int depth) +{ + setTextureWidth(width); + setTextureWidth(height); + setTextureWidth(depth); +} + +/*! + * \return the actual texture data width. When the texture format is QImage::Format_Indexed8, + * this is textureWidth aligned to 32bit boundary. Otherwise this is four times textureWidth. + */ +int QCustom3DVolume::textureDataWidth() const +{ + int dataWidth = dptrc()->m_textureWidth; + + if (dptrc()->m_textureFormat == QImage::Format_Indexed8) + dataWidth += dataWidth % 4; + else + dataWidth *= 4; + + return dataWidth; +} + +/*! \property QCustom3DVolume::sliceIndexX + * + * The X dimension index into the texture data indicating which vertical slice to show. + * Setting any dimension to negative indicates no slice for that dimension is drawn. + * If all dimensions are negative, no slices are drawn and the volume is drawn normally. + * Defaults to \c{-1}. + * + * \sa textureData + */ +void QCustom3DVolume::setSliceIndexX(int value) +{ + if (dptr()->m_sliceIndexX != value) { + dptr()->m_sliceIndexX = value; + dptr()->m_dirtyBitsVolume.sliceIndicesDirty = true; + emit sliceIndexXChanged(value); + emit dptr()->needUpdate(); + } +} + +int QCustom3DVolume::sliceIndexX() const +{ + return dptrc()->m_sliceIndexX; +} + +/*! \property QCustom3DVolume::sliceIndexY + * + * The Y dimension index into the texture data indicating which horizontal slice to show. + * Setting any dimension to negative indicates no slice for that dimension is drawn. + * If all dimensions are negative, no slices are drawn and the volume is drawn normally. + * Defaults to \c{-1}. + * + * \sa textureData + */ +void QCustom3DVolume::setSliceIndexY(int value) +{ + if (dptr()->m_sliceIndexY != value) { + dptr()->m_sliceIndexY = value; + dptr()->m_dirtyBitsVolume.sliceIndicesDirty = true; + emit sliceIndexYChanged(value); + emit dptr()->needUpdate(); + } +} + +int QCustom3DVolume::sliceIndexY() const +{ + return dptrc()->m_sliceIndexY; +} + +/*! \property QCustom3DVolume::sliceIndexZ + * + * The Z dimension index into the texture data indicating which vertical slice to show. + * Setting any dimension to negative indicates no slice for that dimension is drawn. + * If all dimensions are negative, no slices are drawn and the volume is drawn normally. + * Defaults to \c{-1}. + * + * \sa textureData + */ +void QCustom3DVolume::setSliceIndexZ(int value) +{ + if (dptr()->m_sliceIndexZ != value) { + dptr()->m_sliceIndexZ = value; + dptr()->m_dirtyBitsVolume.sliceIndicesDirty = true; + emit sliceIndexZChanged(value); + emit dptr()->needUpdate(); + } +} + +int QCustom3DVolume::sliceIndexZ() const +{ + return dptrc()->m_sliceIndexZ; +} + +/*! + * A convenience function for setting all three slice indices (\a x, \a y, and \a z) at once. + * + * \sa textureData + */ +void QCustom3DVolume::setSliceIndices(int x, int y, int z) +{ + setSliceIndexX(x); + setSliceIndexY(y); + setSliceIndexZ(z); +} + +/*! \property QCustom3DVolume::colorTable + * + * The array containing the colors for indexed texture formats. If the texture format is not + * indexed, this array is not used and can be empty. + * + * Defaults to \c{0}. + * + * \sa textureData, textureFormat, QImage::colorTable() + */ +void QCustom3DVolume::setColorTable(const QVector &colors) +{ + if (dptr()->m_colorTable != colors) { + dptr()->m_colorTable = colors; + dptr()->m_dirtyBitsVolume.colorTableDirty = true; + emit colorTableChanged(); + emit dptr()->needUpdate(); + } +} + +QVector QCustom3DVolume::colorTable() const +{ + return dptrc()->m_colorTable; +} + +/*! \property QCustom3DVolume::textureData + * + * The array containing the texture data in the format specified by textureFormat. + * The size of this array must be at least + * (\c{textureDataWidth * textureHeight * textureDepth * texture format color depth in bytes}). + * + * A 3D texture is defined by a stack of 2D subtextures. Each subtexture must be of identical size + * (\c{textureDataWidth * textureHeight}), and the depth of the stack is defined by textureDepth + * property. Each 2D texture data is identical to a QImage data with the same format, so + * QImage::bits() can be used to supply the data for each subtexture. + * + * Ownership of the new array transfers to QCustom3DVolume instance. + * If another array is set, the previous array is deleted. + * If the same array is set again, it is assumed that the array contents have been changed and the + * graph rendering is triggered. + * + * \note Each X-line of the data needs to be 32bit aligned. If the textureFormat is + * QImage::Format_Indexed8 and textureWidth is not divisible by four, padding bytes need + * to be added to each X-line of the \a data. You can get the padded byte count with + * textureDataWidth() function. + * + * Defaults to \c{0}. + * + * \sa colorTable, textureFormat, setSubTextureData(), textureDataWidth() + */ +void QCustom3DVolume::setTextureData(QVector *data) +{ + if (dptr()->m_textureData != data) + delete dptr()->m_textureData; + + // Even if the pointer is same as previously, consider this property changed, as the values + // can be changed unbeknownst to us via the array pointer. + dptr()->m_textureData = data; + dptr()->m_dirtyBitsVolume.textureDataDirty = true; + emit textureDataChanged(data); + emit dptr()->needUpdate(); +} + +/*! + * This function creates a new texture data array from an array of \a images and sets it as + * textureData for this volume object. The texture dimensions are also set according to image + * and array dimensions. All of the images in the array must be the same size. If the images are not + * all in QImage::Format_Indexed8 format, all texture data will be converted into + * QImage::Format_ARGB32 format. If the images are in QImage::Format_Indexed8 format, the colorTable + * for the entire volume will be taken from the first image. + * + * \return pointer to the newly created array. + * + * \sa textureData, textureWidth, textureHeight, textureDepth, textureFormat + */ +QVector *QCustom3DVolume::createTextureData(const QVector &images) +{ + int imageCount = images.size(); + if (imageCount) { + QImage *currentImage = images.at(0); + int imageWidth = currentImage->width(); + int imageHeight = currentImage->height(); + QImage::Format imageFormat = currentImage->format(); + bool convert = false; + if (imageFormat != QImage::Format_Indexed8 && imageFormat != QImage::Format_ARGB32) { + convert = true; + imageFormat = QImage::Format_ARGB32; + } else { + for (int i = 0; i < imageCount; i++) { + currentImage = images.at(i); + if (imageWidth != currentImage->width() || imageHeight != currentImage->height()) { + qWarning() << __FUNCTION__ << "Not all images were of the same size."; + setTextureData(0); + setTextureWidth(0); + setTextureHeight(0); + setTextureDepth(0); + return 0; + + } + if (currentImage->format() != imageFormat) { + convert = true; + imageFormat = QImage::Format_ARGB32; + break; + } + } + } + int colorBytes = (imageFormat == QImage::Format_Indexed8) ? 1 : 4; + int imageByteWidth = (imageFormat == QImage::Format_Indexed8) + ? currentImage->bytesPerLine() : imageWidth; + int frameSize = imageByteWidth * imageHeight * colorBytes; + QVector *newTextureData = new QVector; + newTextureData->resize(frameSize * imageCount); + uchar *texturePtr = newTextureData->data(); + QImage convertedImage; + + for (int i = 0; i < imageCount; i++) { + currentImage = images.at(i); + if (convert) { + convertedImage = currentImage->convertToFormat(imageFormat); + currentImage = &convertedImage; + } + memcpy(texturePtr, static_cast(currentImage->bits()), frameSize); + texturePtr += frameSize; + } + + if (imageFormat == QImage::Format_Indexed8) + setColorTable(images.at(0)->colorTable()); + setTextureData(newTextureData); + setTextureFormat(imageFormat); + setTextureWidth(imageWidth); + setTextureHeight(imageHeight); + setTextureDepth(imageCount); + } else { + setTextureData(0); + setTextureWidth(0); + setTextureHeight(0); + setTextureDepth(0); + } + return dptr()->m_textureData; +} + +QVector *QCustom3DVolume::textureData() const +{ + return dptrc()->m_textureData; +} + +/*! + * This function allows setting a single 2D subtexture of the 3D texture. + * The \a depthIndex parameter specifies the subtexture to set. + * The texture\a data must be in the format specified by textureFormat property and have size of + * (\c{textureDataWidth * textureHeight * texture format color depth in bytes}). + * + * \note Each X-line of the data needs to be 32bit aligned. If the textureFormat is + * QImage::Format_Indexed8 and textureWidth is not divisible by four, padding bytes need + * to be added to each X-line of the \a data. + * + * \sa textureData + */ +void QCustom3DVolume::setSubTextureData(int depthIndex, const uchar *data) +{ + if (data) { + int frameSize = textureDataWidth() * dptr()->m_textureHeight; + int startIndex = depthIndex * frameSize; + + if (depthIndex >= dptr()->m_textureDepth + || (startIndex + frameSize) > dptr()->m_textureData->size()) { + qWarning() << __FUNCTION__ << "Attempted to set invalid subtexture."; + } else { + void *subTexPtr = dptr()->m_textureData->data() + startIndex; + memcpy(subTexPtr, static_cast(data), frameSize); + dptr()->m_dirtyBitsVolume.textureDataDirty = true; + emit textureDataChanged(dptr()->m_textureData); + emit dptr()->needUpdate(); + } + } else { + qWarning() << __FUNCTION__ << "Tried to set null data."; + } +} + +/*! + * This function allows setting a single 2D subtexture of the 3D texture to a source \a image; + * The \a depthIndex parameter specifies the subtexture to set. + * The image must be in the format specified by the textureFormat property if the textureFormat + * is indexed. If the textureFormat is QImage::Format_ARGB32, the image is converted to that format. + * The image must have the size of (\c{textureWidth * textureHeight}). + * + * \sa textureData + */ +void QCustom3DVolume::setSubTextureData(int depthIndex, const QImage &image) +{ + if (image.width() == dptr()->m_textureWidth + && image.height() == dptr()->m_textureHeight + && (image.format() == dptr()->m_textureFormat + || dptr()->m_textureFormat == QImage::Format_ARGB32)) { + QImage convertedImage; + if (dptr()->m_textureFormat == QImage::Format_ARGB32 + && image.format() != QImage::Format_ARGB32) { + convertedImage = image.convertToFormat(QImage::Format_ARGB32); + } else { + convertedImage = image; + } + setSubTextureData(depthIndex, convertedImage.bits()); + } else { + qWarning() << __FUNCTION__ << "Invalid image size or format."; + } +} + +/*! \property QCustom3DVolume::textureFormat + * + * The format of the textureData. Only two formats are supported currently: + * QImage::Format_Indexed8 and QImage::Format_ARGB32. If an indexed format is specified, colorTable + * must also be set. + * Defaults to QImage::Format_ARGB32. + * + * \sa colorTable, textureData + */ +void QCustom3DVolume::setTextureFormat(QImage::Format format) +{ + if (format == QImage::Format_ARGB32 || format == QImage::Format_Indexed8) { + if (dptr()->m_textureFormat != format) { + dptr()->m_textureFormat = format; + dptr()->m_dirtyBitsVolume.textureFormatDirty = true; + emit textureFormatChanged(format); + emit dptr()->needUpdate(); + } + } else { + qWarning() << __FUNCTION__ << "Attempted to set invalid texture format."; + } +} + +QImage::Format QCustom3DVolume::textureFormat() const +{ + return dptrc()->m_textureFormat; +} + + +/*! + * \internal + */ +QCustom3DVolumePrivate *QCustom3DVolume::dptr() +{ + return static_cast(d_ptr.data()); +} + +/*! + * \internal + */ +const QCustom3DVolumePrivate *QCustom3DVolume::dptrc() const +{ + return static_cast(d_ptr.data()); +} + +QCustom3DVolumePrivate::QCustom3DVolumePrivate(QCustom3DVolume *q) : + QCustom3DItemPrivate(q), + m_sliceIndexX(-1), + m_sliceIndexY(-1), + m_sliceIndexZ(-1), + m_textureWidth(0), + m_textureHeight(0), + m_textureDepth(0), + m_textureFormat(QImage::Format_ARGB32), + m_textureData(0) +{ + m_isVolumeItem = true; + m_meshFile = QStringLiteral(":/defaultMeshes/barFull"); +} + +QCustom3DVolumePrivate::QCustom3DVolumePrivate(QCustom3DVolume *q, const QVector3D &position, + const QVector3D &scaling, + const QQuaternion &rotation, + int textureWidth, int textureHeight, + int textureDepth, QVector *textureData, + QImage::Format textureFormat, + const QVector &colorTable) : + QCustom3DItemPrivate(q, QStringLiteral(":/defaultMeshes/barFull"), position, scaling, rotation), + m_sliceIndexX(-1), + m_sliceIndexY(-1), + m_sliceIndexZ(-1), + m_textureWidth(textureWidth), + m_textureHeight(textureHeight), + m_textureDepth(textureDepth), + m_textureFormat(textureFormat), + m_colorTable(colorTable), + m_textureData(textureData) +{ + m_isVolumeItem = true; + m_shadowCasting = false; + + if (m_textureWidth < 0) + m_textureWidth = 0; + if (m_textureHeight < 0) + m_textureHeight = 0; + if (m_textureDepth < 0) + m_textureDepth = 0; + + if (m_colorTable.size() != 256) + m_colorTable.clear(); + + if (m_textureFormat != QImage::Format_Indexed8) + m_textureFormat = QImage::Format_ARGB32; + +} + +QCustom3DVolumePrivate::~QCustom3DVolumePrivate() +{ + delete m_textureData; +} + +void QCustom3DVolumePrivate::resetDirtyBits() +{ + QCustom3DItemPrivate::resetDirtyBits(); + + m_dirtyBitsVolume.textureDimensionsDirty = false; + m_dirtyBitsVolume.sliceIndicesDirty = false; + m_dirtyBitsVolume.colorTableDirty = false; + m_dirtyBitsVolume.textureDataDirty = false; + m_dirtyBitsVolume.textureFormatDirty = false; +} + +QCustom3DVolume *QCustom3DVolumePrivate::qptr() +{ + return static_cast(q_ptr); +} + +QT_END_NAMESPACE_DATAVISUALIZATION diff --git a/src/datavisualization/data/qcustom3dvolume.h b/src/datavisualization/data/qcustom3dvolume.h new file mode 100644 index 00000000..498922e8 --- /dev/null +++ b/src/datavisualization/data/qcustom3dvolume.h @@ -0,0 +1,106 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +#ifndef QCUSTOM3DVOLUME_H +#define QCUSTOM3DVOLUME_H + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE_DATAVISUALIZATION + +class QCustom3DVolumePrivate; + +class QT_DATAVISUALIZATION_EXPORT QCustom3DVolume : public QCustom3DItem +{ + Q_OBJECT + Q_PROPERTY(int textureWidth READ textureWidth WRITE setTextureWidth NOTIFY textureWidthChanged) + Q_PROPERTY(int textureHeight READ textureHeight WRITE setTextureHeight NOTIFY textureHeightChanged) + Q_PROPERTY(int textureDepth READ textureDepth WRITE setTextureDepth NOTIFY textureDepthChanged) + Q_PROPERTY(int sliceIndexX READ sliceIndexX WRITE setSliceIndexX NOTIFY sliceIndexXChanged) + Q_PROPERTY(int sliceIndexY READ sliceIndexY WRITE setSliceIndexY NOTIFY sliceIndexYChanged) + Q_PROPERTY(int sliceIndexZ READ sliceIndexZ WRITE setSliceIndexZ NOTIFY sliceIndexZChanged) + Q_PROPERTY(QVector colorTable READ colorTable WRITE setColorTable NOTIFY colorTableChanged) + Q_PROPERTY(QVector *textureData READ textureData WRITE setTextureData NOTIFY textureDataChanged) + Q_PROPERTY(QImage::Format textureFormat READ textureFormat WRITE setTextureFormat NOTIFY textureFormatChanged) + +public: + + explicit QCustom3DVolume(QObject *parent = 0); + explicit QCustom3DVolume(const QVector3D &position, const QVector3D &scaling, + const QQuaternion &rotation, int textureWidth, + int textureHeight, int textureDepth, + QVector *textureData, QImage::Format textureFormat, + const QVector &colorTable, QObject *parent = 0); + virtual ~QCustom3DVolume(); + + void setTextureWidth(int value); + int textureWidth() const; + void setTextureHeight(int value); + int textureHeight() const; + void setTextureDepth(int value); + int textureDepth() const; + void setTextureDimensions(int width, int height, int depth); + int textureDataWidth() const; + + void setSliceIndexX(int value); + int sliceIndexX() const; + void setSliceIndexY(int value); + int sliceIndexY() const; + void setSliceIndexZ(int value); + int sliceIndexZ() const; + void setSliceIndices(int x, int y, int z); + + void setColorTable(const QVector &colors); + QVector colorTable() const; + + void setTextureData(QVector *data); + QVector *createTextureData(const QVector &images); + QVector *textureData() const; + void setSubTextureData(int depthIndex, const uchar *data); + void setSubTextureData(int depthIndex, const QImage &image); + + void setTextureFormat(QImage::Format format); + QImage::Format textureFormat() const; + +signals: + void textureWidthChanged(int value); + void textureHeightChanged(int value); + void textureDepthChanged(int value); + void sliceIndexXChanged(int value); + void sliceIndexYChanged(int value); + void sliceIndexZChanged(int value); + void colorTableChanged(); + void textureDataChanged(QVector *data); + void textureFormatChanged(QImage::Format format); + +protected: + QCustom3DVolumePrivate *dptr(); + const QCustom3DVolumePrivate *dptrc() const; + +private: + Q_DISABLE_COPY(QCustom3DVolume) + + friend class Abstract3DRenderer; +}; + +QT_END_NAMESPACE_DATAVISUALIZATION + +#endif diff --git a/src/datavisualization/data/qcustom3dvolume_p.h b/src/datavisualization/data/qcustom3dvolume_p.h new file mode 100644 index 00000000..69dd1eb2 --- /dev/null +++ b/src/datavisualization/data/qcustom3dvolume_p.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the QtDataVisualization API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. + +#ifndef QCUSTOM3DVOLUME_P_H +#define QCUSTOM3DVOLUME_P_H + +#include "qcustom3dvolume.h" +#include "qcustom3ditem_p.h" + +QT_BEGIN_NAMESPACE_DATAVISUALIZATION + +struct QCustomVolumeDirtyBitField { + bool textureDimensionsDirty : 1; + bool sliceIndicesDirty : 1; + bool colorTableDirty : 1; + bool textureDataDirty : 1; + bool textureFormatDirty : 1; + + QCustomVolumeDirtyBitField() + : textureDimensionsDirty(false), + sliceIndicesDirty(false), + colorTableDirty(false), + textureDataDirty(false), + textureFormatDirty(false) + { + } +}; + +class QCustom3DVolumePrivate : public QCustom3DItemPrivate +{ + Q_OBJECT + +public: + QCustom3DVolumePrivate(QCustom3DVolume *q); + QCustom3DVolumePrivate(QCustom3DVolume *q, const QVector3D &position, const QVector3D &scaling, + const QQuaternion &rotation, int textureWidth, + int textureHeight, int textureDepth, QVector *textureData, + QImage::Format textureFormat, const QVector &colorTable); + virtual ~QCustom3DVolumePrivate(); + + void resetDirtyBits(); + + QCustom3DVolume *qptr(); + +public: + int m_textureWidth; + int m_textureHeight; + int m_textureDepth; + int m_sliceIndexX; + int m_sliceIndexY; + int m_sliceIndexZ; + + QImage::Format m_textureFormat; + QVector m_colorTable; + QVector *m_textureData; + + QCustomVolumeDirtyBitField m_dirtyBitsVolume; + +private: + friend class QCustom3DVolume; +}; + +QT_END_NAMESPACE_DATAVISUALIZATION + +#endif diff --git a/src/datavisualization/engine/abstract3drenderer.cpp b/src/datavisualization/engine/abstract3drenderer.cpp index 14cf7109..cbb90af0 100644 --- a/src/datavisualization/engine/abstract3drenderer.cpp +++ b/src/datavisualization/engine/abstract3drenderer.cpp @@ -24,6 +24,7 @@ #include "shaderhelper_p.h" #include "qcustom3ditem_p.h" #include "qcustom3dlabel_p.h" +#include "qcustom3dvolume_p.h" #include @@ -57,6 +58,8 @@ Abstract3DRenderer::Abstract3DRenderer(Abstract3DController *controller) m_selectionLabelItem(0), m_visibleSeriesCount(0), m_customItemShader(0), + m_volumeTextureShader(0), + m_volumeTextureSliceShader(0), m_useOrthoProjection(false), m_xFlipped(false), m_yFlipped(false), @@ -96,6 +99,8 @@ Abstract3DRenderer::~Abstract3DRenderer() delete m_cachedTheme; delete m_selectionLabelItem; delete m_customItemShader; + delete m_volumeTextureShader; + delete m_volumeTextureSliceShader; foreach (SeriesRenderCache *cache, m_renderCacheList) { cache->cleanup(m_textureHelper); @@ -196,6 +201,20 @@ void Abstract3DRenderer::initCustomItemShaders(const QString &vertexShader, m_customItemShader->initialize(); } +void Abstract3DRenderer::initVolumeTextureShaders(const QString &vertexShader, + const QString &fragmentShader, + const QString &sliceShader) +{ + if (m_volumeTextureShader) + delete m_volumeTextureShader; + m_volumeTextureShader = new ShaderHelper(this, vertexShader, fragmentShader); + m_volumeTextureShader->initialize(); + if (m_volumeTextureSliceShader) + delete m_volumeTextureSliceShader; + m_volumeTextureSliceShader = new ShaderHelper(this, vertexShader, sliceShader); + m_volumeTextureSliceShader->initialize(); +} + void Abstract3DRenderer::updateTheme(Q3DTheme *theme) { // Synchronize the controller theme with renderer @@ -279,6 +298,9 @@ void Abstract3DRenderer::reInitShaders() initCustomItemShaders(QStringLiteral(":/shaders/vertexTexture"), QStringLiteral(":/shaders/fragmentTexture")); } + initVolumeTextureShaders(QStringLiteral(":/shaders/vertexTexture3D"), + QStringLiteral(":/shaders/fragmentTexture3D"), + QStringLiteral(":/shaders/fragmentTexture3DSlice")); #else initGradientShaders(QStringLiteral(":/shaders/vertex"), QStringLiteral(":/shaders/fragmentColorOnYES2")); @@ -906,6 +928,7 @@ CustomRenderItem *Abstract3DRenderer::addCustomItem(QCustom3DItem *item) QVector3D scaling = item->scaling(); QImage textureImage = item->d_ptr->textureImage(); bool facingCamera = false; + GLuint texture; if (item->d_ptr->m_isLabelItem) { QCustom3DLabel *labelItem = static_cast(item); float pointSize = labelItem->font().pointSizeF(); @@ -925,13 +948,39 @@ CustomRenderItem *Abstract3DRenderer::addCustomItem(QCustom3DItem *item) scaling.setY(scaling.y() * textureImage.height() * scaledFontSize); // Check if facing camera facingCamera = labelItem->isFacingCamera(); +#if !defined(QT_OPENGL_ES_2) + } else if (item->d_ptr->m_isVolumeItem) { + QCustom3DVolume *volumeItem = static_cast(item); + newItem->setTextureWidth(volumeItem->textureWidth()); + newItem->setTextureHeight(volumeItem->textureHeight()); + newItem->setTextureDepth(volumeItem->textureDepth()); + if (volumeItem->textureFormat() == QImage::Format_Indexed8) + newItem->setColorTable(volumeItem->colorTable()); + newItem->setTextureFormat(volumeItem->textureFormat()); + newItem->setVolume(true); + newItem->setBlendNeeded(true); + texture = m_textureHelper->create3DTexture(volumeItem->textureData(), + volumeItem->textureWidth(), + volumeItem->textureHeight(), + volumeItem->textureDepth(), + volumeItem->textureFormat()); + newItem->setSliceIndexX(volumeItem->sliceIndexX()); + newItem->setSliceIndexY(volumeItem->sliceIndexY()); + newItem->setSliceIndexZ(volumeItem->sliceIndexZ()); +#endif } newItem->setScaling(scaling); newItem->setRotation(item->rotation()); newItem->setPosition(item->position()); newItem->setPositionAbsolute(item->isPositionAbsolute()); - newItem->setBlendNeeded(textureImage.hasAlphaChannel()); - GLuint texture = m_textureHelper->create2DTexture(textureImage, true, true, true); +#if !defined(QT_OPENGL_ES_2) + // In OpenGL ES we simply draw volumes as regular custom item placeholders. + if (!item->d_ptr->m_isVolumeItem) +#endif + { + newItem->setBlendNeeded(textureImage.hasAlphaChannel()); + texture = m_textureHelper->create2DTexture(textureImage, true, true, true); + } newItem->setTexture(texture); item->d_ptr->clearTextureImage(); QVector3D translation = convertPositionToTranslation(item->position(), @@ -996,12 +1045,17 @@ void Abstract3DRenderer::updateCustomItem(CustomRenderItem *renderItem) m_cachedTheme->isLabelBorderEnabled()); textureImage = item->d_ptr->textureImage(); } + } else +#if !defined(QT_OPENGL_ES_2) + if (!item->d_ptr->m_isVolumeItem) +#endif + { + renderItem->setBlendNeeded(textureImage.hasAlphaChannel()); + GLuint oldTexture = renderItem->texture(); + m_textureHelper->deleteTexture(&oldTexture); + GLuint texture = m_textureHelper->create2DTexture(textureImage, true, true, true); + renderItem->setTexture(texture); } - renderItem->setBlendNeeded(textureImage.hasAlphaChannel()); - GLuint oldTexture = renderItem->texture(); - m_textureHelper->deleteTexture(&oldTexture); - GLuint texture = m_textureHelper->create2DTexture(textureImage, true, true, true); - renderItem->setTexture(texture); item->d_ptr->clearTextureImage(); item->d_ptr->m_dirtyBits.textureDirty = false; } @@ -1028,6 +1082,39 @@ void Abstract3DRenderer::updateCustomItem(CustomRenderItem *renderItem) renderItem->setFacingCamera(labelItem->isFacingCamera()); labelItem->dptr()->m_facingCameraDirty = false; } +#if !defined(QT_OPENGL_ES_2) + } else if (item->d_ptr->m_isVolumeItem) { + QCustom3DVolume *volumeItem = static_cast(item); + if (volumeItem->dptr()->m_dirtyBitsVolume.colorTableDirty) { + renderItem->setColorTable(volumeItem->colorTable()); + volumeItem->dptr()->m_dirtyBitsVolume.colorTableDirty = false; + } + if (volumeItem->dptr()->m_dirtyBitsVolume.textureDimensionsDirty + || volumeItem->dptr()->m_dirtyBitsVolume.textureDataDirty + || volumeItem->dptr()->m_dirtyBitsVolume.textureFormatDirty) { + GLuint oldTexture = renderItem->texture(); + m_textureHelper->deleteTexture(&oldTexture); + GLuint texture = m_textureHelper->create3DTexture(volumeItem->textureData(), + volumeItem->textureWidth(), + volumeItem->textureHeight(), + volumeItem->textureDepth(), + volumeItem->textureFormat()); + renderItem->setTexture(texture); + renderItem->setTextureWidth(volumeItem->textureWidth()); + renderItem->setTextureHeight(volumeItem->textureHeight()); + renderItem->setTextureDepth(volumeItem->textureDepth()); + renderItem->setTextureFormat(volumeItem->textureFormat()); + volumeItem->dptr()->m_dirtyBitsVolume.textureDimensionsDirty = false; + volumeItem->dptr()->m_dirtyBitsVolume.textureDataDirty = false; + volumeItem->dptr()->m_dirtyBitsVolume.textureFormatDirty = false; + } + if (volumeItem->dptr()->m_dirtyBitsVolume.sliceIndicesDirty) { + renderItem->setSliceIndexX(volumeItem->sliceIndexX()); + renderItem->setSliceIndexY(volumeItem->sliceIndexY()); + renderItem->setSliceIndexZ(volumeItem->sliceIndexZ()); + volumeItem->dptr()->m_dirtyBitsVolume.sliceIndicesDirty = false; + } +#endif } } @@ -1042,7 +1129,9 @@ void Abstract3DRenderer::updateCustomItemPositions() #ifdef USE_REFLECTIONS void Abstract3DRenderer::drawCustomItems(RenderingState state, - ShaderHelper *shader, + ShaderHelper *regularShader, + ShaderHelper *volumeShader, + ShaderHelper *volumeSliceShader, const QMatrix4x4 &viewMatrix, const QMatrix4x4 &projectionViewMatrix, const QMatrix4x4 &depthProjectionViewMatrix, @@ -1051,7 +1140,9 @@ void Abstract3DRenderer::drawCustomItems(RenderingState state, GLfloat reflection) #else void Abstract3DRenderer::drawCustomItems(RenderingState state, - ShaderHelper *shader, + ShaderHelper *regularShader, + ShaderHelper *volumeShader, + ShaderHelper *volumeSliceShader, const QMatrix4x4 &viewMatrix, const QMatrix4x4 &projectionViewMatrix, const QMatrix4x4 &depthProjectionViewMatrix, @@ -1059,11 +1150,17 @@ void Abstract3DRenderer::drawCustomItems(RenderingState state, GLfloat shadowQuality) #endif { +#if defined(QT_OPENGL_ES_2) + Q_UNUSED(volumeShader) + Q_UNUSED(volumeSliceShader) +#endif if (m_customRenderCache.isEmpty()) return; + ShaderHelper *shader = regularShader; + shader->bind(); + if (RenderingNormal == state) { - shader->bind(); shader->setUniformValue(shader->lightP(), m_cachedScene->activeLight()->position()); shader->setUniformValue(shader->ambientS(), m_cachedTheme->ambientLightStrength()); shader->setUniformValue(shader->lightColor(), @@ -1137,6 +1234,22 @@ void Abstract3DRenderer::drawCustomItems(RenderingState state, if (RenderingNormal == state) { // Normal render +#if !defined(QT_OPENGL_ES_2) + ShaderHelper *prevShader = shader; + if (item->isVolume()) { + if (item->sliceIndexX() >= 0 + || item->sliceIndexY() >= 0 + || item->sliceIndexZ() >= 0) { + shader = volumeSliceShader; + } else { + shader = volumeShader; + } + } else { + shader = regularShader; + } + if (shader != prevShader) + shader->bind(); +#endif shader->setUniformValue(shader->model(), modelMatrix); shader->setUniformValue(shader->MVP(), MVPMatrix); shader->setUniformValue(shader->nModel(), itModelMatrix.inverted().transposed()); @@ -1144,14 +1257,17 @@ void Abstract3DRenderer::drawCustomItems(RenderingState state, if (item->isBlendNeeded()) { glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glDisable(GL_CULL_FACE); +#if !defined(QT_OPENGL_ES_2) + if (!item->isVolume()) +#endif + glDisable(GL_CULL_FACE); } else { glDisable(GL_BLEND); glEnable(GL_CULL_FACE); } #if !defined(QT_OPENGL_ES_2) - if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { + if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone && !item->isVolume()) { // Set shadow shader bindings shader->setUniformValue(shader->shadowQ(), shadowQuality); shader->setUniformValue(shader->depth(), depthProjectionViewMatrix * modelMatrix); @@ -1164,8 +1280,34 @@ void Abstract3DRenderer::drawCustomItems(RenderingState state, #endif { // Set shadowless shader bindings - shader->setUniformValue(shader->lightS(), m_cachedTheme->lightStrength()); - m_drawer->drawObject(shader, item->mesh(), item->texture()); +#if !defined(QT_OPENGL_ES_2) + if (item->isVolume()) { + // Volume shaders repurpose light position for camera position relative to item + QVector3D cameraPos = m_cachedScene->activeCamera()->position(); + cameraPos = MVPMatrix.inverted().map(cameraPos); + shader->setUniformValue(shader->cameraPositionRelativeToModel(), -cameraPos); + GLint color8Bit = (item->textureFormat() == QImage::Format_Indexed8) ? 1 : 0; + if (color8Bit) { + shader->setUniformValueArray(shader->colorIndex(), + item->colorTable().constData(), 256); + } + shader->setUniformValue(shader->color8Bit(), color8Bit); + if (shader == volumeSliceShader) { + QVector3D slices((float(item->sliceIndexX()) + 0.5f) + / float(item->textureWidth()) * 2.0 - 1.0, + (float(item->sliceIndexY()) + 0.5f) + / float(item->textureHeight()) * 2.0 - 1.0, + (float(item->sliceIndexZ()) + 0.5f) + / float(item->textureDepth()) * 2.0 - 1.0); + shader->setUniformValue(shader->volumeSliceIndices(), slices); + } + m_drawer->drawObject(shader, item->mesh(), 0, 0, item->texture()); + } else +#endif + { + shader->setUniformValue(shader->lightS(), m_cachedTheme->lightStrength()); + m_drawer->drawObject(shader, item->mesh(), item->texture()); + } } } else if (RenderingSelection == state) { // Selection render @@ -1203,6 +1345,9 @@ void Abstract3DRenderer::drawRadialGrid(ShaderHelper *shader, float yFloorLinePo const QMatrix4x4 &projectionViewMatrix, const QMatrix4x4 &depthMatrix) { +#if defined(QT_OPENGL_ES_2) + Q_UNUSED(depthMatrix) +#endif static QVector lineRotations; if (!lineRotations.size()) { lineRotations.resize(polarGridRoundness); @@ -1264,6 +1409,9 @@ void Abstract3DRenderer::drawAngularGrid(ShaderHelper *shader, float yFloorLineP const QMatrix4x4 &projectionViewMatrix, const QMatrix4x4 &depthMatrix) { +#if defined(QT_OPENGL_ES_2) + Q_UNUSED(depthMatrix) +#endif float halfRatio((m_polarRadius + (labelMargin / 2.0f)) / 2.0f); QVector3D gridLineScaler(gridLineWidth, gridLineWidth, halfRatio); int gridLineCount = m_axisCacheX.gridLineCount(); diff --git a/src/datavisualization/engine/abstract3drenderer_p.h b/src/datavisualization/engine/abstract3drenderer_p.h index 50370954..7874a1ad 100644 --- a/src/datavisualization/engine/abstract3drenderer_p.h +++ b/src/datavisualization/engine/abstract3drenderer_p.h @@ -94,6 +94,9 @@ public: const QString &fragmentShader) = 0; virtual void initCustomItemShaders(const QString &vertexShader, const QString &fragmentShader); + virtual void initVolumeTextureShaders(const QString &vertexShader, + const QString &fragmentShader, + const QString &sliceShader); virtual void updateAxisType(QAbstract3DAxis::AxisOrientation orientation, QAbstract3DAxis::AxisType type); virtual void updateAxisTitle(QAbstract3DAxis::AxisOrientation orientation, @@ -145,13 +148,15 @@ public: QString &selectionLabel(); #ifdef USE_REFLECTIONS - void drawCustomItems(RenderingState state, ShaderHelper *shader, + void drawCustomItems(RenderingState state, ShaderHelper *regularShader, + ShaderHelper *volumeShader, ShaderHelper *volumeSliceShader, const QMatrix4x4 &viewMatrix, const QMatrix4x4 &projectionViewMatrix, const QMatrix4x4 &depthProjectionViewMatrix, GLuint depthTexture, GLfloat shadowQuality, GLfloat reflection = 1.0f); #else - void drawCustomItems(RenderingState state, ShaderHelper *shader, + void drawCustomItems(RenderingState state, ShaderHelper *regularShader, + ShaderHelper *volumeShader, ShaderHelper *volumeSliceShader, const QMatrix4x4 &viewMatrix, const QMatrix4x4 &projectionViewMatrix, const QMatrix4x4 &depthProjectionViewMatrix, @@ -247,6 +252,8 @@ protected: int m_visibleSeriesCount; ShaderHelper *m_customItemShader; + ShaderHelper *m_volumeTextureShader; + ShaderHelper *m_volumeTextureSliceShader; bool m_useOrthoProjection; bool m_xFlipped; diff --git a/src/datavisualization/engine/bars3drenderer.cpp b/src/datavisualization/engine/bars3drenderer.cpp index 38fa3147..085b881e 100644 --- a/src/datavisualization/engine/bars3drenderer.cpp +++ b/src/datavisualization/engine/bars3drenderer.cpp @@ -1093,9 +1093,11 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) } } - Abstract3DRenderer::drawCustomItems(RenderingDepth, m_depthShader, viewMatrix, - projectionViewMatrix, depthProjectionViewMatrix, - m_depthTexture, m_shadowQualityToShader); + Abstract3DRenderer::drawCustomItems(RenderingDepth, m_depthShader, m_volumeTextureShader, + m_volumeTextureSliceShader, viewMatrix, + projectionViewMatrix, + depthProjectionViewMatrix, m_depthTexture, + m_shadowQualityToShader); // Disable drawing to depth framebuffer (= enable drawing to screen) glBindFramebuffer(GL_FRAMEBUFFER, defaultFboHandle); @@ -1179,7 +1181,9 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) } } glCullFace(GL_BACK); - Abstract3DRenderer::drawCustomItems(RenderingSelection, m_selectionShader, viewMatrix, + Abstract3DRenderer::drawCustomItems(RenderingSelection, m_selectionShader, + m_volumeTextureShader, m_volumeTextureSliceShader, + viewMatrix, projectionViewMatrix, depthProjectionViewMatrix, m_depthTexture, m_shadowQualityToShader); drawLabels(true, activeCamera, viewMatrix, projectionMatrix); @@ -1267,7 +1271,8 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) drawGridLines(depthProjectionViewMatrix, projectionViewMatrix, viewMatrix); // Draw custom items - Abstract3DRenderer::drawCustomItems(RenderingNormal, m_customItemShader, viewMatrix, + Abstract3DRenderer::drawCustomItems(RenderingNormal, m_customItemShader, m_volumeTextureShader, + m_volumeTextureSliceShader, viewMatrix, projectionViewMatrix, depthProjectionViewMatrix, m_depthTexture, m_shadowQualityToShader); diff --git a/src/datavisualization/engine/drawer.cpp b/src/datavisualization/engine/drawer.cpp index b8726840..4191efc4 100644 --- a/src/datavisualization/engine/drawer.cpp +++ b/src/datavisualization/engine/drawer.cpp @@ -93,8 +93,11 @@ QFont Drawer::font() const } void Drawer::drawObject(ShaderHelper *shader, AbstractObjectHelper *object, GLuint textureId, - GLuint depthTextureId) + GLuint depthTextureId, GLuint textureId3D) { +#if defined(QT_OPENGL_ES_2) + Q_UNUSED(textureId3D) +#endif if (textureId) { // Activate texture glActiveTexture(GL_TEXTURE0); @@ -108,6 +111,14 @@ void Drawer::drawObject(ShaderHelper *shader, AbstractObjectHelper *object, GLui glBindTexture(GL_TEXTURE_2D, depthTextureId); shader->setUniformValue(shader->shadow(), 1); } +#if !defined(QT_OPENGL_ES_2) + if (textureId3D) { + // Activate texture + glActiveTexture(GL_TEXTURE2); + glBindTexture(GL_TEXTURE_3D, textureId3D); + shader->setUniformValue(shader->texture(), 2); + } +#endif // 1st attribute buffer : vertices glEnableVertexAttribArray(shader->posAtt()); @@ -139,6 +150,12 @@ void Drawer::drawObject(ShaderHelper *shader, AbstractObjectHelper *object, GLui glDisableVertexAttribArray(shader->posAtt()); // Release textures +#if !defined(QT_OPENGL_ES_2) + if (textureId3D) { + glActiveTexture(GL_TEXTURE2); + glBindTexture(GL_TEXTURE_3D, 0); + } +#endif if (depthTextureId) { glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, 0); diff --git a/src/datavisualization/engine/drawer_p.h b/src/datavisualization/engine/drawer_p.h index ffcea315..709503dd 100644 --- a/src/datavisualization/engine/drawer_p.h +++ b/src/datavisualization/engine/drawer_p.h @@ -75,7 +75,7 @@ public: inline GLfloat scaledFontSize() const { return m_scaledFontSize; } void drawObject(ShaderHelper *shader, AbstractObjectHelper *object, GLuint textureId = 0, - GLuint depthTextureId = 0); + GLuint depthTextureId = 0, GLuint textureId3D = 0); void drawSelectionObject(ShaderHelper *shader, AbstractObjectHelper *object); void drawSurfaceGrid(ShaderHelper *shader, SurfaceObject *object); void drawPoint(ShaderHelper *shader); diff --git a/src/datavisualization/engine/engine.qrc b/src/datavisualization/engine/engine.qrc index f788b5df..936ed53f 100644 --- a/src/datavisualization/engine/engine.qrc +++ b/src/datavisualization/engine/engine.qrc @@ -59,5 +59,8 @@ shaders/texture.vert shaders/surfaceTexturedShadowFlat.frag shaders/surfaceTexturedFlat.frag + shaders/texture3d.frag + shaders/texture3d.vert + shaders/texture3dslice.frag diff --git a/src/datavisualization/engine/scatter3drenderer.cpp b/src/datavisualization/engine/scatter3drenderer.cpp index 09225b99..13f6e02c 100644 --- a/src/datavisualization/engine/scatter3drenderer.cpp +++ b/src/datavisualization/engine/scatter3drenderer.cpp @@ -538,9 +538,11 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) } } - Abstract3DRenderer::drawCustomItems(RenderingDepth, m_depthShader, viewMatrix, - projectionViewMatrix, depthProjectionViewMatrix, - m_depthTexture, m_shadowQualityToShader); + Abstract3DRenderer::drawCustomItems(RenderingDepth, m_depthShader, m_volumeTextureShader, + m_volumeTextureSliceShader, viewMatrix, + projectionViewMatrix, + depthProjectionViewMatrix, m_depthTexture, + m_shadowQualityToShader); // Disable drawing to framebuffer (= enable drawing to screen) glBindFramebuffer(GL_FRAMEBUFFER, defaultFboHandle); @@ -641,9 +643,11 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) } } - Abstract3DRenderer::drawCustomItems(RenderingSelection, m_selectionShader, viewMatrix, - projectionViewMatrix, depthProjectionViewMatrix, - m_depthTexture, m_shadowQualityToShader); + Abstract3DRenderer::drawCustomItems(RenderingSelection, m_selectionShader, + m_volumeTextureShader, m_volumeTextureSliceShader, + viewMatrix, projectionViewMatrix, + depthProjectionViewMatrix, m_depthTexture, + m_shadowQualityToShader); drawLabels(true, activeCamera, viewMatrix, projectionMatrix); @@ -1407,7 +1411,8 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) } } - Abstract3DRenderer::drawCustomItems(RenderingNormal, m_customItemShader, viewMatrix, + Abstract3DRenderer::drawCustomItems(RenderingNormal, m_customItemShader, m_volumeTextureShader, + m_volumeTextureSliceShader, viewMatrix, projectionViewMatrix, depthProjectionViewMatrix, m_depthTexture, m_shadowQualityToShader); diff --git a/src/datavisualization/engine/shaders/default.frag b/src/datavisualization/engine/shaders/default.frag index c03b1054..0276ed95 100644 --- a/src/datavisualization/engine/shaders/default.frag +++ b/src/datavisualization/engine/shaders/default.frag @@ -33,5 +33,6 @@ void main() { materialDiffuseColor * lightStrength * pow(cosTheta, 2) / distance + materialSpecularColor * lightStrength * pow(cosAlpha, 5) / distance; gl_FragColor.a = color_mdl.a; + gl_FragColor = clamp(gl_FragColor, 0.0, 1.0); } diff --git a/src/datavisualization/engine/shaders/texture3d.frag b/src/datavisualization/engine/shaders/texture3d.frag new file mode 100644 index 00000000..054b59cb --- /dev/null +++ b/src/datavisualization/engine/shaders/texture3d.frag @@ -0,0 +1,70 @@ +#version 120 + +varying highp vec3 pos; + +uniform highp sampler3D textureSampler; +uniform highp vec3 cameraPositionRelativeToModel; +uniform highp vec4 colorIndex[256]; +uniform highp int color8Bit; + +const float maxDist = sqrt(2.0); +const int sampleCount = 1024; +const float alphaThreshold = 0.001; +void main() { + // Raytrace into volume, need to sample pixels along the eye ray until we hit opacity 1 + + // Find out where ray intersects the object + highp vec3 rayDir = -(cameraPositionRelativeToModel - pos); + highp vec3 invRayDir = 1.0 / rayDir; + highp vec3 minCorner = vec3(-1.0); + highp vec3 maxCorner = vec3(1.0); + highp vec3 t1 = invRayDir * (minCorner - pos); + highp vec3 t2 = invRayDir * (maxCorner - pos); + highp vec3 tmin = min(t1, t2); + highp vec3 tmax = max(t1, t2); + highp vec2 t = max(tmin.xx, tmin.yz); + t = min(tmax.xx, tmax.yz); + float tFar = min(t.x, t.y); + highp vec3 rayStart = pos; + // Flip Y and Z so QImage bits work directly for texture and first image is in the front + rayStart.yz = -rayStart.yz; + highp vec3 rayStop = pos + rayDir * tFar; + rayStop.yz = -rayStop.yz; + + // Convert intersections to texture coords + rayStart = 0.5 * (rayStart + 1.0); + rayStop = 0.5 * (rayStop + 1.0); + + highp vec3 curPos = rayStart; + highp float fullDist = distance(rayStop, rayStart); + highp float stepSize = maxDist / float(sampleCount); // TODO: Stepsize needs to be improved + highp vec3 step = normalize(rayStop - rayStart) * stepSize; + highp float totalDist = 0.0; + highp float totalAlpha = 0.0; + highp vec4 destColor = vec4(0, 0, 0, 0); + highp vec4 curColor = vec4(0, 0, 0, 0); + highp vec3 curRgb = vec3(0, 0, 0); + highp float curAlpha = 0.0; + + for (int i = 0; i < sampleCount; i++) { + curColor = texture3D(textureSampler, curPos); + if (color8Bit != 0) + curColor = colorIndex[int(curColor.r * 255.0)]; + + curAlpha = curColor.a; + if (curAlpha > alphaThreshold) { + curRgb = curColor.rgb * curAlpha * (1.0 - totalAlpha); + destColor.rgb += curRgb; + totalAlpha += curAlpha; + } + curPos += step; + totalDist += stepSize; + if (totalDist > fullDist || totalAlpha >= 1.0) { + break; + } + } + + destColor.a = totalAlpha; + gl_FragColor = clamp(destColor, 0.0, 1.0); +} + diff --git a/src/datavisualization/engine/shaders/texture3d.vert b/src/datavisualization/engine/shaders/texture3d.vert new file mode 100644 index 00000000..cad1ce06 --- /dev/null +++ b/src/datavisualization/engine/shaders/texture3d.vert @@ -0,0 +1,12 @@ +uniform highp mat4 MVP; + +attribute highp vec3 vertexPosition_mdl; +attribute highp vec2 vertexUV; +attribute highp vec3 vertexNormal_mdl; + +varying highp vec3 pos; + +void main() { + gl_Position = MVP * vec4(vertexPosition_mdl, 1.0); + pos = vertexPosition_mdl; +} diff --git a/src/datavisualization/engine/shaders/texture3dslice.frag b/src/datavisualization/engine/shaders/texture3dslice.frag new file mode 100644 index 00000000..641b32a5 --- /dev/null +++ b/src/datavisualization/engine/shaders/texture3dslice.frag @@ -0,0 +1,119 @@ +#version 120 + +varying highp vec3 pos; + +uniform highp sampler3D textureSampler; +uniform highp vec3 cameraPositionRelativeToModel; +uniform highp vec3 volumeSliceIndices; +uniform highp vec4 colorIndex[256]; +uniform highp int color8Bit; + +const highp vec3 xPlaneNormal = vec3(1.0, 0, 0); +const highp vec3 yPlaneNormal = vec3(0, 1.0, 0); +const highp vec3 zPlaneNormal = vec3(0, 0, 1.0); + +const float alphaThreshold = 0.001; +void main() { + // Raytrace into volume, need to sample pixels along the eye ray until we hit opacity 1 + + // Find out where ray intersects the slice planes + highp vec3 rayDir = -(cameraPositionRelativeToModel - pos); + highp vec3 rayStart = pos; + // Flip Y and Z so QImage bits work directly for texture and first image is in the front + rayStart.yz = -rayStart.yz; + rayDir.yz = -rayDir.yz; + highp vec3 invRayDir = 1.0 / rayDir; + highp vec3 minCorner = vec3(-1.0); + highp vec3 maxCorner = vec3(1.0); + highp vec3 t1 = invRayDir * (minCorner - rayStart); + highp vec3 t2 = invRayDir * (maxCorner - rayStart); + highp vec3 tmin = min(t1, t2); + highp vec3 tmax = max(t1, t2); + highp vec2 t = max(tmin.xx, tmin.yz); + t = min(tmax.xx, tmax.yz); + float tFar = min(t.x, t.y); + + highp vec3 xPoint = vec3(volumeSliceIndices.x, 0, 0); + highp vec3 yPoint = vec3(0, volumeSliceIndices.y, 0); + highp vec3 zPoint = vec3(0, 0, volumeSliceIndices.z); + highp float firstD = tFar + 1.0; + highp float secondD = firstD; + highp float thirdD = firstD; + if (volumeSliceIndices.x >= -1.0) { + highp float dx = dot(xPoint - rayStart, xPlaneNormal) / dot(rayDir, xPlaneNormal); + if (dx >= 0.0 && dx <= tFar) + firstD = min(dx, firstD); + } + if (volumeSliceIndices.y >= -1.0) { + highp float dy = dot(yPoint - rayStart, yPlaneNormal) / dot(rayDir, yPlaneNormal); + if (dy >= 0.0 && dy <= tFar) { + if (dy < firstD) { + secondD = firstD; + firstD = dy; + } else { + secondD = dy; + } + } + } + if (volumeSliceIndices.z >= -1.0) { + highp float dz = dot(zPoint - rayStart, zPlaneNormal) / dot(rayDir, zPlaneNormal); + if (dz >= 0.0) { + if (dz < firstD && dz <= tFar) { + thirdD = secondD; + secondD = firstD; + firstD = dz; + } else if (dz < secondD){ + thirdD = secondD; + secondD = dz; + } else { + thirdD = dz; + } + } + } + + highp vec4 destColor = vec4(0.0, 0.0, 0.0, 0.0); + highp float totalAlpha = 0.0; + highp vec3 curRgb = vec3(0, 0, 0); + + // Convert intersection to texture coords + + if (firstD <= tFar) { + highp vec3 firstTex = rayStart + rayDir * firstD; + firstTex = 0.5 * (firstTex + 1.0); + highp vec4 firstColor = texture3D(textureSampler, firstTex); + if (color8Bit != 0) + firstColor = colorIndex[int(firstColor.r * 255.0)]; + + if (firstColor.a > alphaThreshold) { + destColor.rgb = firstColor.rgb * firstColor.a; + totalAlpha = firstColor.a; + } + if (secondD <= tFar && totalAlpha < 1.0) { + highp vec3 secondTex = rayStart + rayDir * secondD; + secondTex = 0.5 * (secondTex + 1.0); + highp vec4 secondColor = texture3D(textureSampler, secondTex); + if (color8Bit != 0) + secondColor = colorIndex[int(secondColor.r * 255.0)]; + if (secondColor.a > alphaThreshold) { + curRgb = secondColor.rgb * secondColor.a * (1.0 - totalAlpha); + destColor.rgb += curRgb; + totalAlpha += secondColor.a; + } + if (thirdD <= tFar && totalAlpha < 1.0) { + highp vec3 thirdTex = rayStart + rayDir * thirdD; + thirdTex = 0.5 * (thirdTex + 1.0); + highp vec4 thirdColor = texture3D(textureSampler, thirdTex); + if (color8Bit != 0) + thirdColor = colorIndex[int(thirdColor.r * 255.0)]; + if (thirdColor.a > alphaThreshold) { + curRgb = thirdColor.rgb * thirdColor.a * (1.0 - totalAlpha); + destColor.rgb += curRgb; + totalAlpha += thirdColor.a; + } + } + } + } + destColor.a = totalAlpha; + gl_FragColor = clamp(destColor, 0.0, 1.0); +} + diff --git a/src/datavisualization/engine/surface3drenderer.cpp b/src/datavisualization/engine/surface3drenderer.cpp index 1607f66a..17be3278 100644 --- a/src/datavisualization/engine/surface3drenderer.cpp +++ b/src/datavisualization/engine/surface3drenderer.cpp @@ -1196,9 +1196,11 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) glEnable(GL_CULL_FACE); glCullFace(GL_FRONT); - Abstract3DRenderer::drawCustomItems(RenderingDepth, m_depthShader, viewMatrix, - projectionViewMatrix, depthProjectionViewMatrix, - m_depthTexture, m_shadowQualityToShader); + Abstract3DRenderer::drawCustomItems(RenderingDepth, m_depthShader, m_volumeTextureShader, + m_volumeTextureSliceShader, viewMatrix, + projectionViewMatrix, + depthProjectionViewMatrix, m_depthTexture, + m_shadowQualityToShader); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, m_depthModelTexture, 0); @@ -1232,9 +1234,11 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) glDisableVertexAttribArray(m_depthShader->posAtt()); - Abstract3DRenderer::drawCustomItems(RenderingDepth, m_depthShader, viewMatrix, - projectionViewMatrix, depthProjectionViewMatrix, - m_depthTexture, m_shadowQualityToShader); + Abstract3DRenderer::drawCustomItems(RenderingDepth, m_depthShader, m_volumeTextureShader, + m_volumeTextureSliceShader, viewMatrix, + projectionViewMatrix, + depthProjectionViewMatrix, m_depthTexture, + m_shadowQualityToShader); // Disable drawing to depth framebuffer (= enable drawing to screen) glBindFramebuffer(GL_FRAMEBUFFER, defaultFboHandle); @@ -1287,7 +1291,9 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) } } m_surfaceGridShader->bind(); - Abstract3DRenderer::drawCustomItems(RenderingSelection, m_surfaceGridShader, viewMatrix, + Abstract3DRenderer::drawCustomItems(RenderingSelection, m_surfaceGridShader, + m_volumeTextureShader, m_volumeTextureSliceShader, + viewMatrix, projectionViewMatrix, depthProjectionViewMatrix, m_depthTexture, m_shadowQualityToShader); drawLabels(true, activeCamera, viewMatrix, projectionMatrix); @@ -1858,7 +1864,8 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) } } - Abstract3DRenderer::drawCustomItems(RenderingNormal, m_customItemShader, viewMatrix, + Abstract3DRenderer::drawCustomItems(RenderingNormal, m_customItemShader, m_volumeTextureShader, + m_volumeTextureSliceShader, viewMatrix, projectionViewMatrix, depthProjectionViewMatrix, m_depthTexture, m_shadowQualityToShader); diff --git a/src/datavisualization/utils/shaderhelper.cpp b/src/datavisualization/utils/shaderhelper.cpp index 7fb237c6..3c3d93ac 100644 --- a/src/datavisualization/utils/shaderhelper.cpp +++ b/src/datavisualization/utils/shaderhelper.cpp @@ -93,6 +93,10 @@ void ShaderHelper::initialize() m_gradientMinUniform = m_program->uniformLocation("gradMin"); m_gradientHeightUniform = m_program->uniformLocation("gradHeight"); m_lightColorUniform = m_program->uniformLocation("lightColor"); + m_volumeSliceIndices = m_program->uniformLocation("volumeSliceIndices"); + m_colorIndex = m_program->uniformLocation("colorIndex"); + m_cameraPositionRelativeToModel = m_program->uniformLocation("cameraPositionRelativeToModel"); + m_color8Bit = m_program->uniformLocation("color8Bit"); m_initialized = true; } @@ -150,6 +154,11 @@ void ShaderHelper::setUniformValue(GLuint uniform, GLint value) m_program->setUniformValue(uniform, value); } +void ShaderHelper::setUniformValueArray(GLuint uniform, const QVector4D *values, int count) +{ + m_program->setUniformValueArray(uniform, values, count); +} + GLuint ShaderHelper::MVP() { if (!m_initialized) @@ -255,6 +264,34 @@ GLuint ShaderHelper::lightColor() return m_lightColorUniform; } +GLuint ShaderHelper::volumeSliceIndices() +{ + if (!m_initialized) + qFatal("Shader not initialized"); + return m_volumeSliceIndices; +} + +GLuint ShaderHelper::colorIndex() +{ + if (!m_initialized) + qFatal("Shader not initialized"); + return m_colorIndex; +} + +GLuint ShaderHelper::cameraPositionRelativeToModel() +{ + if (!m_initialized) + qFatal("Shader not initialized"); + return m_cameraPositionRelativeToModel; +} + +GLuint ShaderHelper::color8Bit() +{ + if (!m_initialized) + qFatal("Shader not initialized"); + return m_color8Bit; +} + GLuint ShaderHelper::posAtt() { if (!m_initialized) diff --git a/src/datavisualization/utils/shaderhelper_p.h b/src/datavisualization/utils/shaderhelper_p.h index fdef0dff..67e8a2f3 100644 --- a/src/datavisualization/utils/shaderhelper_p.h +++ b/src/datavisualization/utils/shaderhelper_p.h @@ -57,6 +57,7 @@ class ShaderHelper void setUniformValue(GLuint uniform, const QMatrix4x4 &value); void setUniformValue(GLuint uniform, GLfloat value); void setUniformValue(GLuint uniform, GLint value); + void setUniformValueArray(GLuint uniform, const QVector4D *values, int count); GLuint MVP(); GLuint view(); @@ -73,6 +74,10 @@ class ShaderHelper GLuint gradientMin(); GLuint gradientHeight(); GLuint lightColor(); + GLuint volumeSliceIndices(); + GLuint colorIndex(); + GLuint cameraPositionRelativeToModel(); + GLuint color8Bit(); GLuint posAtt(); GLuint uvAtt(); @@ -107,6 +112,10 @@ class ShaderHelper GLuint m_gradientMinUniform; GLuint m_gradientHeightUniform; GLuint m_lightColorUniform; + GLuint m_volumeSliceIndices; + GLuint m_colorIndex; + GLuint m_cameraPositionRelativeToModel; + GLuint m_color8Bit; GLboolean m_initialized; }; diff --git a/src/datavisualization/utils/texturehelper.cpp b/src/datavisualization/utils/texturehelper.cpp index 616c1981..5e21b5cd 100644 --- a/src/datavisualization/utils/texturehelper.cpp +++ b/src/datavisualization/utils/texturehelper.cpp @@ -21,11 +21,25 @@ #include #include +#include QT_BEGIN_NAMESPACE_DATAVISUALIZATION +// Defined in shaderhelper.cpp +extern void discardDebugMsgs(QtMsgType type, const QMessageLogContext &context, const QString &msg); + TextureHelper::TextureHelper() { +#if !defined(QT_OPENGL_ES_2) + // Discard warnings about deprecated functions + QtMessageHandler handler = qInstallMessageHandler(discardDebugMsgs); + + m_openGlFunctions_2_1 = new QOpenGLFunctions_2_1; + m_openGlFunctions_2_1->initializeOpenGLFunctions(); + + // Restore original message handler + qInstallMessageHandler(handler); +#endif initializeOpenGLFunctions(); } @@ -73,8 +87,54 @@ GLuint TextureHelper::create2DTexture(const QImage &image, bool useTrilinearFilt if (clampY) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glBindTexture(GL_TEXTURE_2D, 0); + + return textureId; +} + +#if !defined(QT_OPENGL_ES_2) +GLuint TextureHelper::create3DTexture(const QVector *data, int width, int height, int depth, + QImage::Format dataFormat) +{ + if (!width || !height || !depth) + return 0; + + glEnable(GL_TEXTURE_3D); + + GLuint textureId; + glGenTextures(1, &textureId); + glBindTexture(GL_TEXTURE_3D, textureId); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + + GLenum status = glGetError(); + // glGetError docs advise to call glGetError in loop to clear all error flags + while (status) + status = glGetError(); + + GLint internalFormat = 4; + GLint format = GL_BGRA; + if (dataFormat == QImage::Format_Indexed8) { + internalFormat = 1; + format = GL_RED; + // Align width to 32bits + width = width + width % 4; + } + m_openGlFunctions_2_1->glTexImage3D(GL_TEXTURE_3D, 0, internalFormat, width, height, depth, 0, + format, GL_UNSIGNED_BYTE, data->constData()); + + status = glGetError(); + if (status) + qWarning() << __FUNCTION__ << "3D texture creation failed:" << status; + + glBindTexture(GL_TEXTURE_3D, 0); + glDisable(GL_TEXTURE_3D); + return textureId; } +#endif GLuint TextureHelper::createCubeMapTexture(const QImage &image, bool useTrilinearFiltering) { diff --git a/src/datavisualization/utils/texturehelper_p.h b/src/datavisualization/utils/texturehelper_p.h index c20f293c..c1dfe8c9 100644 --- a/src/datavisualization/utils/texturehelper_p.h +++ b/src/datavisualization/utils/texturehelper_p.h @@ -32,6 +32,10 @@ #include "datavisualizationglobal_p.h" #include #include +#if !defined(QT_OPENGL_ES_2) +// 3D Textures are not supported by ES set +# include +#endif QT_BEGIN_NAMESPACE_DATAVISUALIZATION @@ -44,6 +48,10 @@ class TextureHelper : protected QOpenGLFunctions // Ownership of created texture is transferred to caller GLuint create2DTexture(const QImage &image, bool useTrilinearFiltering = false, bool convert = true, bool smoothScale = true, bool clampY = false); +#if !defined(QT_OPENGL_ES_2) + GLuint create3DTexture(const QVector *data, int width, int height, int depth, + QImage::Format dataFormat); +#endif GLuint createCubeMapTexture(const QImage &image, bool useTrilinearFiltering = false); // Returns selection texture and inserts generated framebuffers to framebuffer parameters GLuint createSelectionTexture(const QSize &size, GLuint &frameBuffer, GLuint &depthBuffer); @@ -61,6 +69,9 @@ class TextureHelper : protected QOpenGLFunctions void convertToGLFormatHelper(QImage &dstImage, const QImage &srcImage, GLenum texture_format); QRgb qt_gl_convertToGLFormatHelper(QRgb src_pixel, GLenum texture_format); +#if !defined(QT_OPENGL_ES_2) + QOpenGLFunctions_2_1 *m_openGlFunctions_2_1; +#endif friend class Bars3DRenderer; friend class Surface3DRenderer; friend class Scatter3DRenderer; diff --git a/tests/tests.pro b/tests/tests.pro index 8bbdc3f2..21036b59 100644 --- a/tests/tests.pro +++ b/tests/tests.pro @@ -16,7 +16,8 @@ SUBDIRS += barstest \ directional \ qmlmultiwindow \ itemmodeltest \ - qmlmultitest + qmlmultitest \ + volumetrictest #SUBDIRS += kinectsurface diff --git a/tests/volumetrictest/logo.png b/tests/volumetrictest/logo.png new file mode 100644 index 00000000..1e7ed4cf Binary files /dev/null and b/tests/volumetrictest/logo.png differ diff --git a/tests/volumetrictest/main.cpp b/tests/volumetrictest/main.cpp new file mode 100644 index 00000000..46edf576 --- /dev/null +++ b/tests/volumetrictest/main.cpp @@ -0,0 +1,105 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +#include "volumetrictest.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int main(int argc, char **argv) +{ + QApplication app(argc, argv); + Q3DScatter *graph = new Q3DScatter(); + QWidget *container = QWidget::createWindowContainer(graph); + + QSize screenSize = graph->screen()->size(); + container->setMinimumSize(QSize(screenSize.width() / 2, screenSize.height() / 1.5)); + container->setMaximumSize(screenSize); + container->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + container->setFocusPolicy(Qt::StrongFocus); + + QWidget *widget = new QWidget; + QHBoxLayout *hLayout = new QHBoxLayout(widget); + QVBoxLayout *vLayout = new QVBoxLayout(); + hLayout->addWidget(container, 1); + hLayout->addLayout(vLayout); + + widget->setWindowTitle(QStringLiteral("Volumetric TEST")); + + QCheckBox *sliceXCheckBox = new QCheckBox(widget); + sliceXCheckBox->setText(QStringLiteral("Slice volume on X axis")); + sliceXCheckBox->setChecked(false); + QCheckBox *sliceYCheckBox = new QCheckBox(widget); + sliceYCheckBox->setText(QStringLiteral("Slice volume on Y axis")); + sliceYCheckBox->setChecked(false); + QCheckBox *sliceZCheckBox = new QCheckBox(widget); + sliceZCheckBox->setText(QStringLiteral("Slice volume on Z axis")); + sliceZCheckBox->setChecked(false); + + QSlider *sliceXSlider = new QSlider(Qt::Horizontal, widget); + sliceXSlider->setMinimum(0); + sliceXSlider->setMaximum(1024); + sliceXSlider->setValue(512); + sliceXSlider->setEnabled(true); + QSlider *sliceYSlider = new QSlider(Qt::Horizontal, widget); + sliceYSlider->setMinimum(0); + sliceYSlider->setMaximum(1024); + sliceYSlider->setValue(512); + sliceYSlider->setEnabled(true); + QSlider *sliceZSlider = new QSlider(Qt::Horizontal, widget); + sliceZSlider->setMinimum(0); + sliceZSlider->setMaximum(1024); + sliceZSlider->setValue(512); + sliceZSlider->setEnabled(true); + + QLabel *fpsLabel = new QLabel(QStringLiteral("Fps: "), widget); + + vLayout->addWidget(fpsLabel); + vLayout->addWidget(sliceXCheckBox); + vLayout->addWidget(sliceXSlider); + vLayout->addWidget(sliceYCheckBox); + vLayout->addWidget(sliceYSlider); + vLayout->addWidget(sliceZCheckBox); + vLayout->addWidget(sliceZSlider, 1, Qt::AlignTop); + + VolumetricModifier *modifier = new VolumetricModifier(graph); + modifier->setFpsLabel(fpsLabel); + + QObject::connect(sliceXCheckBox, &QCheckBox::stateChanged, modifier, + &VolumetricModifier::sliceX); + QObject::connect(sliceYCheckBox, &QCheckBox::stateChanged, modifier, + &VolumetricModifier::sliceY); + QObject::connect(sliceZCheckBox, &QCheckBox::stateChanged, modifier, + &VolumetricModifier::sliceZ); + QObject::connect(sliceXSlider, &QSlider::valueChanged, modifier, + &VolumetricModifier::adjustSliceX); + QObject::connect(sliceYSlider, &QSlider::valueChanged, modifier, + &VolumetricModifier::adjustSliceY); + QObject::connect(sliceZSlider, &QSlider::valueChanged, modifier, + &VolumetricModifier::adjustSliceZ); + + widget->show(); + return app.exec(); +} diff --git a/tests/volumetrictest/volumetrictest.cpp b/tests/volumetrictest/volumetrictest.cpp new file mode 100644 index 00000000..04aad052 --- /dev/null +++ b/tests/volumetrictest/volumetrictest.cpp @@ -0,0 +1,282 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +#include "volumetrictest.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace QtDataVisualization; + +const int imageCount = 512; + +VolumetricModifier::VolumetricModifier(Q3DScatter *scatter) + : m_graph(scatter), + m_volumeItem(0), + m_sliceIndexX(0), + m_sliceIndexY(0), + m_sliceIndexZ(0) +{ + m_graph->activeTheme()->setType(Q3DTheme::ThemeQt); + m_graph->setShadowQuality(QAbstract3DGraph::ShadowQualityNone); + m_graph->scene()->activeCamera()->setCameraPreset(Q3DCamera::CameraPresetFront); + m_graph->setOrthoProjection(true); + //m_graph->scene()->activeCamera()->setTarget(QVector3D(0.5f, 0.5f, 0.5f)); + + createVolume(); + createAnotherVolume(); + + m_graph->addCustomItem(m_volumeItem); + m_graph->addCustomItem(m_volumeItem2); + m_graph->setMeasureFps(true); + + QObject::connect(m_graph->scene()->activeCamera(), &Q3DCamera::zoomLevelChanged, this, + &VolumetricModifier::handleZoomLevelChange); + QObject::connect(m_graph, &QAbstract3DGraph::currentFpsChanged, this, + &VolumetricModifier::handleFpsChange); +} + +VolumetricModifier::~VolumetricModifier() +{ + delete m_graph; +} + +void VolumetricModifier::setFpsLabel(QLabel *fpsLabel) +{ + m_fpsLabel = fpsLabel; +} + +void VolumetricModifier::sliceX(int enabled) +{ + m_volumeItem->setSliceIndexX(enabled ? m_sliceIndexX : -1); + m_volumeItem2->setSliceIndexX(enabled ? m_sliceIndexX : -1); +} + +void VolumetricModifier::sliceY(int enabled) +{ + m_volumeItem->setSliceIndexY(enabled ? m_sliceIndexY : -1); + m_volumeItem2->setSliceIndexY(enabled ? m_sliceIndexY : -1); +} + +void VolumetricModifier::sliceZ(int enabled) +{ + m_volumeItem->setSliceIndexZ(enabled ? m_sliceIndexZ : -1); + m_volumeItem2->setSliceIndexZ(enabled ? m_sliceIndexZ : -1); +} + +void VolumetricModifier::adjustSliceX(int value) +{ + m_sliceIndexX = int(float(value) / (1024.0f / m_volumeItem->textureWidth())); + if (m_sliceIndexX == m_volumeItem->textureWidth()) + m_sliceIndexX--; + qDebug() << "m_sliceIndexX:" << m_sliceIndexX; + if (m_volumeItem->sliceIndexX() != -1) { + m_volumeItem->setSliceIndexX(m_sliceIndexX); + m_volumeItem2->setSliceIndexX(m_sliceIndexX); + } +} + +void VolumetricModifier::adjustSliceY(int value) +{ + m_sliceIndexY = int(float(value) / (1024.0f / m_volumeItem->textureHeight())); + if (m_sliceIndexY == m_volumeItem->textureHeight()) + m_sliceIndexY--; + qDebug() << "m_sliceIndexY:" << m_sliceIndexY; + if (m_volumeItem->sliceIndexY() != -1) { + m_volumeItem->setSliceIndexY(m_sliceIndexY); + m_volumeItem2->setSliceIndexY(m_sliceIndexY); + } +} + +void VolumetricModifier::adjustSliceZ(int value) +{ + m_sliceIndexZ = int(float(value) / (1024.0f / m_volumeItem->textureDepth())); + if (m_sliceIndexZ == m_volumeItem->textureDepth()) + m_sliceIndexZ--; + qDebug() << "m_sliceIndexZ:" << m_sliceIndexZ; + if (m_volumeItem->sliceIndexZ() != -1) { + m_volumeItem->setSliceIndexZ(m_sliceIndexZ); + m_volumeItem2->setSliceIndexZ(m_sliceIndexZ); + } +} + +void VolumetricModifier::handleZoomLevelChange() +{ + // Zooming inside volumetric object causes ugly clipping issues, so restrict zoom level a bit + if (m_graph->scene()->activeCamera()->zoomLevel() > 220) + m_graph->scene()->activeCamera()->setZoomLevel(220); +} + +void VolumetricModifier::handleFpsChange(qreal fps) +{ + const QString fpsFormat = QStringLiteral("Fps: %1"); + int fps10 = int(fps * 10.0); + m_fpsLabel->setText(fpsFormat.arg(QString::number(qreal(fps10) / 10.0))); +} + +void VolumetricModifier::createVolume() +{ + m_volumeItem = new QCustom3DVolume; + m_volumeItem->setTextureFormat(QImage::Format_ARGB32); + m_volumeItem->setRotation(QQuaternion::fromAxisAndAngle(1.0f, 1.0f, 0.0f, 10.0f)); + m_volumeItem->setPosition(QVector3D(-0.5f, 0.0f, 0.0f)); + + QImage logo; + logo.load(QStringLiteral(":/logo.png")); + qDebug() << "image dimensions:" << logo.width() << logo.height() + << logo.byteCount() << (logo.width() * logo.height()) + << logo.bytesPerLine(); + + QVector imageArray(imageCount); + for (int i = 0; i < imageCount; i++) { + QImage *newImage = new QImage(logo); + imageArray[i] = newImage; + } + + m_volumeItem->createTextureData(imageArray); + + m_sliceIndexX = m_volumeItem->textureWidth() / 2; + m_sliceIndexY = m_volumeItem->textureWidth() / 2; + m_sliceIndexZ = m_volumeItem->textureWidth() / 2; + + QVector colorTable = m_volumeItem->colorTable(); + colorTable.append(qRgba(255, 0, 0, 255)); + colorTable.append(qRgba(0, 255, 0, 255)); + colorTable.append(qRgba(0, 0, 255, 255)); + m_volumeItem->setColorTable(colorTable); + int redIndex = colorTable.size() - 3; + int greenIndex = colorTable.size() - 2; + int blueIndex = colorTable.size() - 1; + int width = m_volumeItem->textureDataWidth(); + int height = m_volumeItem->textureHeight(); + int depth = m_volumeItem->textureDepth(); + int frameSize = width * height; + qDebug() << width << height << depth << m_volumeItem->textureData()->size(); + m_volumeItem->setScaling(QVector3D(float(width) / float(depth) * 2.0f, + float(height) / float(depth) * 2.0f, + 2.0f)); + + uchar *data = m_volumeItem->textureData()->data(); + uchar *p = data; + + // Change one picture using subtexture replacement + QImage flipped = logo.mirrored(); + m_volumeItem->setSubTextureData(101, flipped); + + // Red first subtexture +// for (int j = 0; j < height; j++) { +// for (int i = 0; i < width; i++) { +// *p = redIndex; +// p++; +// } +// } + + // Red last subtexture + p = data + frameSize * (imageCount - 1); + for (int j = 0; j < height; j++) { + for (int i = 0; i < width; i++) { + *p = redIndex; + p++; + } + } + + // Blue x = 0 + p = data; + for (int k = 0; k < depth; k++) { + for (int j = 0; j < height; j++) { + *p = blueIndex; + p += width; + } + } + + // Blue x = max + p = data + width - 1; + for (int k = 0; k < depth; k++) { + for (int j = 0; j < height; j++) { + *p = blueIndex; + p += width; + } + } + + // Green y = 0 +// p = data; +// for (int k = 0; k < depth; k++) { +// for (int i = 0; i < width; i++) { +// *p = greenIndex; +// p++; +// } +// p += (frameSize - width); +// } + + // Green y = max + p = data + frameSize - width; + for (int k = 0; k < depth; k++) { + for (int i = 0; i < width; i++) { + *p = greenIndex; + p++; + } + p += (frameSize - width); + } +} + +void VolumetricModifier::createAnotherVolume() +{ + m_volumeItem2 = new QCustom3DVolume; + m_volumeItem2->setTextureFormat(QImage::Format_ARGB32); + m_volumeItem2->setPosition(QVector3D(0.5f, 0.0f, 0.0f)); + + QImage logo; + logo.load(QStringLiteral(":/logo.png")); + //logo = logo.convertToFormat(QImage::Format_ARGB8555_Premultiplied); + qDebug() << "second image dimensions:" << logo.width() << logo.height() + << logo.byteCount() << (logo.width() * logo.height()) + << logo.bytesPerLine(); + + logo.save("d:/qt/goobar.png"); + + QVector imageArray(imageCount); + for (int i = 0; i < imageCount; i++) { + QImage *newImage = new QImage(logo); + imageArray[i] = newImage; + } + + m_volumeItem2->createTextureData(imageArray); + + m_sliceIndexX = m_volumeItem2->textureWidth() / 2; + m_sliceIndexY = m_volumeItem2->textureWidth() / 2; + m_sliceIndexZ = m_volumeItem2->textureWidth() / 2; + + int width = m_volumeItem2->textureWidth(); + int height = m_volumeItem2->textureHeight(); + int depth = m_volumeItem2->textureDepth(); + qDebug() << width << height << depth << m_volumeItem2->textureData()->size(); + m_volumeItem2->setScaling(QVector3D(float(width) / float(depth) * 2.0f, + float(height) / float(depth) * 2.0f, + 2.0f)); + + // Change one picture using subtexture replacement + QImage flipped = logo.mirrored(); + m_volumeItem2->setSubTextureData(101, flipped); +} + diff --git a/tests/volumetrictest/volumetrictest.h b/tests/volumetrictest/volumetrictest.h new file mode 100644 index 00000000..677a1231 --- /dev/null +++ b/tests/volumetrictest/volumetrictest.h @@ -0,0 +1,61 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +#ifndef VOLUMETRICMODIFIER_H +#define VOLUMETRICMODIFIER_H + +#include +#include + +class QLabel; + +using namespace QtDataVisualization; + +class VolumetricModifier : public QObject +{ + Q_OBJECT +public: + explicit VolumetricModifier(Q3DScatter *scatter); + ~VolumetricModifier(); + + void setFpsLabel(QLabel *fpsLabel); + +public slots: + void sliceX(int enabled); + void sliceY(int enabled); + void sliceZ(int enabled); + void adjustSliceX(int value); + void adjustSliceY(int value); + void adjustSliceZ(int value); + void handleZoomLevelChange(); + void handleFpsChange(qreal fps); + +private: + void createVolume(); + void createAnotherVolume(); + + Q3DScatter *m_graph; + QCustom3DVolume *m_volumeItem; + QCustom3DVolume *m_volumeItem2; + int m_sliceIndexX; + int m_sliceIndexY; + int m_sliceIndexZ; + QLabel *m_fpsLabel; +}; + +#endif diff --git a/tests/volumetrictest/volumetrictest.pro b/tests/volumetrictest/volumetrictest.pro new file mode 100644 index 00000000..137e5bab --- /dev/null +++ b/tests/volumetrictest/volumetrictest.pro @@ -0,0 +1,18 @@ +android|ios { + error( "This example is not supported for android or ios." ) +} + +!include( ../tests.pri ) { + error( "Couldn't find the tests.pri file!" ) +} + +SOURCES += main.cpp volumetrictest.cpp +HEADERS += volumetrictest.h + +QT += widgets + +OTHER_FILES += doc/src/* \ + doc/images/* + +RESOURCES += \ + volumetrictest.qrc diff --git a/tests/volumetrictest/volumetrictest.qrc b/tests/volumetrictest/volumetrictest.qrc new file mode 100644 index 00000000..90517ff1 --- /dev/null +++ b/tests/volumetrictest/volumetrictest.qrc @@ -0,0 +1,5 @@ + + + logo.png + + -- cgit v1.2.3 From cf95478ea842cd42a8888b0b74de3d0d0d0233ea Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Mon, 18 Aug 2014 14:05:47 +0300 Subject: Make volume shading sample once per texture layer. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: Ia3a13e2cb8d7dcf744a55dcb827f5cb436a043c4 Reviewed-by: Mika Salmela Reviewed-by: Tomi Korpipää --- .../datavisualization/volumetric/volumetric.cpp | 6 ++-- .../engine/abstract3drenderer.cpp | 12 ++++++++ .../engine/shaders/texture3d.frag | 24 ++++++++++++---- src/datavisualization/utils/shaderhelper.cpp | 32 ++++++++++++++++------ src/datavisualization/utils/shaderhelper_p.h | 12 +++++--- 5 files changed, 65 insertions(+), 21 deletions(-) diff --git a/examples/datavisualization/volumetric/volumetric.cpp b/examples/datavisualization/volumetric/volumetric.cpp index a9233cab..8e61ecf2 100644 --- a/examples/datavisualization/volumetric/volumetric.cpp +++ b/examples/datavisualization/volumetric/volumetric.cpp @@ -102,8 +102,8 @@ void VolumetricModifier::adjustSliceZ(int value) void VolumetricModifier::handleZoomLevelChange() { // Zooming inside volumetric object causes ugly clipping issues, so restrict zoom level a bit - if (m_graph->scene()->activeCamera()->zoomLevel() > 220) - m_graph->scene()->activeCamera()->setZoomLevel(220); + if (m_graph->scene()->activeCamera()->zoomLevel() > 200) + m_graph->scene()->activeCamera()->setZoomLevel(200); } void VolumetricModifier::handleFpsChange(qreal fps) @@ -161,7 +161,7 @@ void VolumetricModifier::createVolume() // Take a slice out of the ellipsoid if (i >= textureSize / 2 || j >= textureSize / 4 || k >= textureSize / 2) { QVector3D distVec = QVector3D(float(k), float(j * 2), float(i)) - midPoint; - float adjLen = qMin(255.0f, (distVec.length() * float(textureSize / 128))); + float adjLen = qMin(255.0f, (distVec.length() * 512.0f / float(textureSize))); if (adjLen < 230) colorIndex = 255 - int(adjLen); else diff --git a/src/datavisualization/engine/abstract3drenderer.cpp b/src/datavisualization/engine/abstract3drenderer.cpp index cbb90af0..8eb1d2ce 100644 --- a/src/datavisualization/engine/abstract3drenderer.cpp +++ b/src/datavisualization/engine/abstract3drenderer.cpp @@ -1300,6 +1300,18 @@ void Abstract3DRenderer::drawCustomItems(RenderingState state, (float(item->sliceIndexZ()) + 0.5f) / float(item->textureDepth()) * 2.0 - 1.0); shader->setUniformValue(shader->volumeSliceIndices(), slices); + } else { + // Precalculate texture dimensions so we can optimize + // ray stepping to hit every texture layer. + QVector3D textureDimensions(float(item->textureWidth()), + float(item->textureHeight()), + float(item->textureDepth())); + shader->setUniformValue(shader->textureDimensions(), textureDimensions); + + int sampleCount = qMax(item->textureWidth(), item->textureHeight()); + sampleCount = qMax(sampleCount, item->textureDepth()); + shader->setUniformValue(shader->sampleCount(), sampleCount); + } m_drawer->drawObject(shader, item->mesh(), 0, 0, item->texture()); } else diff --git a/src/datavisualization/engine/shaders/texture3d.frag b/src/datavisualization/engine/shaders/texture3d.frag index 054b59cb..b9d87fcc 100644 --- a/src/datavisualization/engine/shaders/texture3d.frag +++ b/src/datavisualization/engine/shaders/texture3d.frag @@ -6,10 +6,11 @@ uniform highp sampler3D textureSampler; uniform highp vec3 cameraPositionRelativeToModel; uniform highp vec4 colorIndex[256]; uniform highp int color8Bit; +uniform highp vec3 textureDimensions; +uniform highp int sampleCount; // This is the maximum sample count -const float maxDist = sqrt(2.0); -const int sampleCount = 1024; const float alphaThreshold = 0.001; + void main() { // Raytrace into volume, need to sample pixels along the eye ray until we hit opacity 1 @@ -35,10 +36,21 @@ void main() { rayStart = 0.5 * (rayStart + 1.0); rayStop = 0.5 * (rayStop + 1.0); - highp vec3 curPos = rayStart; - highp float fullDist = distance(rayStop, rayStart); - highp float stepSize = maxDist / float(sampleCount); // TODO: Stepsize needs to be improved - highp vec3 step = normalize(rayStop - rayStart) * stepSize; + highp vec3 ray = rayStop - rayStart; + highp float fullDist = length(ray); + highp float rayX = abs(ray.x) * textureDimensions.x; + highp float rayY = abs(ray.y) * textureDimensions.y; + highp float rayZ = abs(ray.z) * textureDimensions.z; + + highp float maxRayDim = max(rayX, rayY); + maxRayDim = max(maxRayDim, rayZ); + + highp vec3 step = ray / maxRayDim; + highp float stepSize = abs(fullDist / maxRayDim); + + // Offset a fraction of a step so we are not exactly on a texel boundary. + highp vec3 curPos = rayStart - (0.5 * step); + highp float totalDist = 0.0; highp float totalAlpha = 0.0; highp vec4 destColor = vec4(0, 0, 0, 0); diff --git a/src/datavisualization/utils/shaderhelper.cpp b/src/datavisualization/utils/shaderhelper.cpp index 3c3d93ac..3361638a 100644 --- a/src/datavisualization/utils/shaderhelper.cpp +++ b/src/datavisualization/utils/shaderhelper.cpp @@ -93,10 +93,12 @@ void ShaderHelper::initialize() m_gradientMinUniform = m_program->uniformLocation("gradMin"); m_gradientHeightUniform = m_program->uniformLocation("gradHeight"); m_lightColorUniform = m_program->uniformLocation("lightColor"); - m_volumeSliceIndices = m_program->uniformLocation("volumeSliceIndices"); - m_colorIndex = m_program->uniformLocation("colorIndex"); - m_cameraPositionRelativeToModel = m_program->uniformLocation("cameraPositionRelativeToModel"); - m_color8Bit = m_program->uniformLocation("color8Bit"); + m_volumeSliceIndicesUniform = m_program->uniformLocation("volumeSliceIndices"); + m_colorIndexUniform = m_program->uniformLocation("colorIndex"); + m_cameraPositionRelativeToModelUniform = m_program->uniformLocation("cameraPositionRelativeToModel"); + m_color8BitUniform = m_program->uniformLocation("color8Bit"); + m_textureDimensionsUniform = m_program->uniformLocation("textureDimensions"); + m_sampleCountUniform = m_program->uniformLocation("sampleCount"); m_initialized = true; } @@ -268,28 +270,42 @@ GLuint ShaderHelper::volumeSliceIndices() { if (!m_initialized) qFatal("Shader not initialized"); - return m_volumeSliceIndices; + return m_volumeSliceIndicesUniform; } GLuint ShaderHelper::colorIndex() { if (!m_initialized) qFatal("Shader not initialized"); - return m_colorIndex; + return m_colorIndexUniform; } GLuint ShaderHelper::cameraPositionRelativeToModel() { if (!m_initialized) qFatal("Shader not initialized"); - return m_cameraPositionRelativeToModel; + return m_cameraPositionRelativeToModelUniform; } GLuint ShaderHelper::color8Bit() { if (!m_initialized) qFatal("Shader not initialized"); - return m_color8Bit; + return m_color8BitUniform; +} + +GLuint ShaderHelper::textureDimensions() +{ + if (!m_initialized) + qFatal("Shader not initialized"); + return m_textureDimensionsUniform; +} + +GLuint ShaderHelper::sampleCount() +{ + if (!m_initialized) + qFatal("Shader not initialized"); + return m_sampleCountUniform; } GLuint ShaderHelper::posAtt() diff --git a/src/datavisualization/utils/shaderhelper_p.h b/src/datavisualization/utils/shaderhelper_p.h index 67e8a2f3..bc7609bb 100644 --- a/src/datavisualization/utils/shaderhelper_p.h +++ b/src/datavisualization/utils/shaderhelper_p.h @@ -78,6 +78,8 @@ class ShaderHelper GLuint colorIndex(); GLuint cameraPositionRelativeToModel(); GLuint color8Bit(); + GLuint textureDimensions(); + GLuint sampleCount(); GLuint posAtt(); GLuint uvAtt(); @@ -112,10 +114,12 @@ class ShaderHelper GLuint m_gradientMinUniform; GLuint m_gradientHeightUniform; GLuint m_lightColorUniform; - GLuint m_volumeSliceIndices; - GLuint m_colorIndex; - GLuint m_cameraPositionRelativeToModel; - GLuint m_color8Bit; + GLuint m_volumeSliceIndicesUniform; + GLuint m_colorIndexUniform; + GLuint m_cameraPositionRelativeToModelUniform; + GLuint m_color8BitUniform; + GLuint m_textureDimensionsUniform; + GLuint m_sampleCountUniform; GLboolean m_initialized; }; -- cgit v1.2.3 From 1574c9353885ec48dbf9d5d7618e496d00c3862c Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Mon, 18 Aug 2014 14:32:40 +0300 Subject: Show warning label in volumetric example on OpenGL ES MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I29ec58f9485a75a00cb1c60d7c57f4c06f620032 Reviewed-by: Mika Salmela Reviewed-by: Tomi Korpipää --- .../datavisualization/volumetric/volumetric.cpp | 32 +++++++++++++++++----- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/examples/datavisualization/volumetric/volumetric.cpp b/examples/datavisualization/volumetric/volumetric.cpp index 8e61ecf2..0c56374e 100644 --- a/examples/datavisualization/volumetric/volumetric.cpp +++ b/examples/datavisualization/volumetric/volumetric.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -42,15 +43,29 @@ VolumetricModifier::VolumetricModifier(Q3DScatter *scatter) m_graph->scene()->activeCamera()->setCameraPreset(Q3DCamera::CameraPresetFront); m_graph->setOrthoProjection(true); +#if !defined(QT_OPENGL_ES_2) createVolume(); - m_graph->addCustomItem(m_volumeItem); +#else + // OpenGL ES2 doesn't support 3D textures, so show a warning label instead + QCustom3DLabel *warningLabel = new QCustom3DLabel( + "QCustom3DVolume is not supported with OpenGL ES2", + QFont(), + QVector3D(0.0f, 0.5f, 0.0f), + QVector3D(1.5f, 1.5f, 0.0f), + QQuaternion()); + warningLabel->setPositionAbsolute(true); + warningLabel->setFacingCamera(true); + m_graph->addCustomItem(warningLabel); + m_graph->activeTheme()->setLightStrength(1.0f); +#endif m_graph->setMeasureFps(true); QObject::connect(m_graph->scene()->activeCamera(), &Q3DCamera::zoomLevelChanged, this, &VolumetricModifier::handleZoomLevelChange); QObject::connect(m_graph, &QAbstract3DGraph::currentFpsChanged, this, &VolumetricModifier::handleFpsChange); + } VolumetricModifier::~VolumetricModifier() @@ -65,37 +80,40 @@ void VolumetricModifier::setFpsLabel(QLabel *fpsLabel) void VolumetricModifier::sliceX(int enabled) { - m_volumeItem->setSliceIndexX(enabled ? m_sliceIndexX : -1); + if (m_volumeItem) + m_volumeItem->setSliceIndexX(enabled ? m_sliceIndexX : -1); } void VolumetricModifier::sliceY(int enabled) { - m_volumeItem->setSliceIndexY(enabled ? m_sliceIndexY : -1); + if (m_volumeItem) + m_volumeItem->setSliceIndexY(enabled ? m_sliceIndexY : -1); } void VolumetricModifier::sliceZ(int enabled) { - m_volumeItem->setSliceIndexZ(enabled ? m_sliceIndexZ : -1); + if (m_volumeItem) + m_volumeItem->setSliceIndexZ(enabled ? m_sliceIndexZ : -1); } void VolumetricModifier::adjustSliceX(int value) { m_sliceIndexX = value / (1024 / textureSize); - if (m_volumeItem->sliceIndexX() != -1) + if (m_volumeItem && m_volumeItem->sliceIndexX() != -1) m_volumeItem->setSliceIndexX(m_sliceIndexX); } void VolumetricModifier::adjustSliceY(int value) { m_sliceIndexY = value / (1024 / textureSize * 2); - if (m_volumeItem->sliceIndexY() != -1) + if (m_volumeItem && m_volumeItem->sliceIndexY() != -1) m_volumeItem->setSliceIndexY(m_sliceIndexY); } void VolumetricModifier::adjustSliceZ(int value) { m_sliceIndexZ = value / (1024 / textureSize); - if (m_volumeItem->sliceIndexZ() != -1) + if (m_volumeItem && m_volumeItem->sliceIndexZ() != -1) m_volumeItem->setSliceIndexZ(m_sliceIndexZ); } -- cgit v1.2.3 From 68b3ea782608cbcd457cbe1abc0c83aceb356777 Mon Sep 17 00:00:00 2001 From: Mika Salmela Date: Tue, 19 Aug 2014 09:33:53 +0300 Subject: Gradient color style support for complex meshes on static Change-Id: Ie9f135a2f3139a429a451c338e03dcf0ea6b4533 Reviewed-by: Miikka Heikkinen --- src/datavisualization/data/qabstract3dseries.h | 1 + src/datavisualization/engine/qabstract3dgraph.cpp | 2 +- src/datavisualization/engine/scatter3drenderer.cpp | 49 ++++++++- src/datavisualization/engine/scatter3drenderer_p.h | 1 + src/datavisualization/engine/seriesrendercache.cpp | 3 +- src/datavisualization/engine/seriesrendercache_p.h | 3 + .../utils/scatterobjectbufferhelper.cpp | 119 ++++++++++++++++++--- .../utils/scatterobjectbufferhelper_p.h | 11 ++ 8 files changed, 174 insertions(+), 15 deletions(-) diff --git a/src/datavisualization/data/qabstract3dseries.h b/src/datavisualization/data/qabstract3dseries.h index 87c4f3c1..c26c40d1 100644 --- a/src/datavisualization/data/qabstract3dseries.h +++ b/src/datavisualization/data/qabstract3dseries.h @@ -153,6 +153,7 @@ private: friend class Bars3DController; friend class Surface3DController; friend class Surface3DRenderer; + friend class Scatter3DRenderer; friend class Scatter3DController; friend class QBar3DSeries; friend class SeriesRenderCache; diff --git a/src/datavisualization/engine/qabstract3dgraph.cpp b/src/datavisualization/engine/qabstract3dgraph.cpp index 23290d69..3f199dab 100644 --- a/src/datavisualization/engine/qabstract3dgraph.cpp +++ b/src/datavisualization/engine/qabstract3dgraph.cpp @@ -637,7 +637,7 @@ qreal QAbstract3DGraph::aspectRatio() const * * Defines if the rendering optimization is default or static. Default mode provides the full feature set at * reasonable performance. Static is a beta level feature and currently supports only a subset of the - * features on the Scatter graph. Missing features are object gradient for mesh objects, both gradients + * features on the Scatter graph. Missing features are both gradient color styles * for points, and diffuse and specular color on rotations. At this point static is intended just for * introducing a new feature. It optimizes graph rendering and is ideal for large non-changing data * sets. It is slower with dynamic data changes and item rotations. Selection is not optimized, so using it diff --git a/src/datavisualization/engine/scatter3drenderer.cpp b/src/datavisualization/engine/scatter3drenderer.cpp index 13f6e02c..bc3893b9 100644 --- a/src/datavisualization/engine/scatter3drenderer.cpp +++ b/src/datavisualization/engine/scatter3drenderer.cpp @@ -190,6 +190,7 @@ void Scatter3DRenderer::updateData() } if (renderArraySize != cache->oldArraySize() || cache->object()->objectFile() != cache->oldMeshFileName()) { + object->setScaleY(m_scaleY); object->fullLoad(cache, m_dotSizeScale); cache->setOldArraySize(renderArraySize); cache->setOldMeshFileName(cache->object()->objectFile()); @@ -207,9 +208,26 @@ void Scatter3DRenderer::updateData() void Scatter3DRenderer::updateSeries(const QList &seriesList) { + int seriesCount = seriesList.size(); + + // Check OptimizationStatic specific issues before populate marks changeTracker done + if (m_cachedOptimizationHint.testFlag(QAbstract3DGraph::OptimizationStatic)) { + for (int i = 0; i < seriesCount; i++) { + QScatter3DSeries *scatterSeries = static_cast(seriesList[i]); + if (scatterSeries->isVisible()) { + QAbstract3DSeriesChangeBitField &changeTracker = scatterSeries->d_ptr->m_changeTracker; + if (changeTracker.baseGradientChanged || changeTracker.colorStyleChanged) { + ScatterSeriesRenderCache *cache = + static_cast(m_renderCacheList.value(scatterSeries)); + if (cache && cache->mesh() != QAbstract3DSeries::MeshPoint) + cache->setStaticObjectUVDirty(true); + } + } + } + } + Abstract3DRenderer::updateSeries(seriesList); - int seriesCount = seriesList.size(); float maxItemSize = 0.0f; float itemSize = 0.0f; bool noSelection = true; @@ -245,6 +263,12 @@ void Scatter3DRenderer::updateSeries(const QList &seriesLis else m_haveGradientMeshSeries = true; } + + if (cache->staticObjectUVDirty()) { + ScatterObjectBufferHelper *object = cache->bufferObject(); + object->updateUVs(cache); + cache->setStaticObjectUVDirty(false); + } } } m_maxItemSize = maxItemSize; @@ -313,6 +337,28 @@ void Scatter3DRenderer::updateAxisTitleVisibility(QAbstract3DAxis::AxisOrientati calculateSceneScalingFactors(); } +void Scatter3DRenderer::updateOptimizationHint(QAbstract3DGraph::OptimizationHints hint) +{ + Abstract3DRenderer::updateOptimizationHint(hint); + + if (m_cachedOptimizationHint.testFlag(QAbstract3DGraph::OptimizationStatic)) { +#if !defined(QT_OPENGL_ES_2) + if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { + initGradientShaders(QStringLiteral(":/shaders/vertexShadow"), + QStringLiteral(":/shaders/fragmentShadow")); + } else { + initGradientShaders(QStringLiteral(":/shaders/vertexTexture"), + QStringLiteral(":/shaders/fragmentTexture")); + } +#else + initGradientShaders(QStringLiteral(":/shaders/vertexTexture"), + QStringLiteral(":/shaders/fragmentTexture")); +#endif + } else { + Abstract3DRenderer::reInitShaders(); + } +} + void Scatter3DRenderer::resetClickedStatus() { m_clickedIndex = Scatter3DController::invalidSelectionIndex(); @@ -2088,6 +2134,7 @@ void Scatter3DRenderer::initGradientShaders(const QString &vertexShader, { if (m_dotGradientShader) delete m_dotGradientShader; + m_dotGradientShader = new ShaderHelper(this, vertexShader, fragmentShader); m_dotGradientShader->initialize(); } diff --git a/src/datavisualization/engine/scatter3drenderer_p.h b/src/datavisualization/engine/scatter3drenderer_p.h index 5f82bf70..353dc098 100644 --- a/src/datavisualization/engine/scatter3drenderer_p.h +++ b/src/datavisualization/engine/scatter3drenderer_p.h @@ -98,6 +98,7 @@ public: const QStringList &labels); void updateAxisTitleVisibility(QAbstract3DAxis::AxisOrientation orientation, bool visible); + void updateOptimizationHint(QAbstract3DGraph::OptimizationHints hint); QVector3D convertPositionToTranslation(const QVector3D &position, bool isAbsolute); diff --git a/src/datavisualization/engine/seriesrendercache.cpp b/src/datavisualization/engine/seriesrendercache.cpp index dc4b9db3..5fcc97f0 100644 --- a/src/datavisualization/engine/seriesrendercache.cpp +++ b/src/datavisualization/engine/seriesrendercache.cpp @@ -37,7 +37,8 @@ SeriesRenderCache::SeriesRenderCache(QAbstract3DSeries *series, Abstract3DRender m_valid(false), m_visible(false), m_renderer(renderer), - m_objectDirty(true) + m_objectDirty(true), + m_staticObjectUVDirty(false) { } diff --git a/src/datavisualization/engine/seriesrendercache_p.h b/src/datavisualization/engine/seriesrendercache_p.h index 96b61b87..5047d671 100644 --- a/src/datavisualization/engine/seriesrendercache_p.h +++ b/src/datavisualization/engine/seriesrendercache_p.h @@ -71,6 +71,8 @@ public: inline bool isVisible() const { return m_visible; } inline void setDataDirty(bool state) { m_objectDirty = state; } inline bool dataDirty() const { return m_objectDirty; } + inline void setStaticObjectUVDirty(bool state) { m_staticObjectUVDirty = state; } + inline bool staticObjectUVDirty() { return m_staticObjectUVDirty; } protected: QAbstract3DSeries *m_series; @@ -94,6 +96,7 @@ protected: bool m_visible; Abstract3DRenderer *m_renderer; bool m_objectDirty; + bool m_staticObjectUVDirty; }; QT_END_NAMESPACE_DATAVISUALIZATION diff --git a/src/datavisualization/utils/scatterobjectbufferhelper.cpp b/src/datavisualization/utils/scatterobjectbufferhelper.cpp index d68b9df4..0ac1dd87 100644 --- a/src/datavisualization/utils/scatterobjectbufferhelper.cpp +++ b/src/datavisualization/utils/scatterobjectbufferhelper.cpp @@ -26,6 +26,7 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION const GLfloat itemScaler = 3.0f; ScatterObjectBufferHelper::ScatterObjectBufferHelper() + : m_scaleY(0.0f) { m_indicesType = GL_UNSIGNED_INT; } @@ -42,12 +43,15 @@ void ScatterObjectBufferHelper::fullLoad(ScatterSeriesRenderCache *cache, qreal m_indexCount = 0; ObjectHelper *dotObj = cache->object(); - ScatterRenderItemArray &renderArray = cache->renderArray(); + const ScatterRenderItemArray &renderArray = cache->renderArray(); const uint renderArraySize = renderArray.size(); + if (renderArraySize == 0) return; // No use to go forward + uint itemCount = renderArraySize; QQuaternion seriesRotation(cache->meshRotation()); + if (m_meshDataLoaded) { // Delete old data glDeleteBuffers(1, &m_vertexbuffer); @@ -55,15 +59,16 @@ void ScatterObjectBufferHelper::fullLoad(ScatterSeriesRenderCache *cache, qreal glDeleteBuffers(1, &m_normalbuffer); glDeleteBuffers(1, &m_elementbuffer); } + // Index vertices const QVector indices = dotObj->indices(); const QVector indexed_vertices = dotObj->indexedvertices(); const QVector indexed_uvs = dotObj->indexedUVs(); const QVector indexed_normals = dotObj->indexedNormals(); - int indicesCount = indices.count(); - int verticeCount = indexed_vertices.count(); - int uvsCount = indexed_uvs.count(); - int normalsCount = indexed_normals.count(); + const int indicesCount = indices.count(); + const int verticeCount = indexed_vertices.count(); + const int uvsCount = indexed_uvs.count(); + const int normalsCount = indexed_normals.count(); float itemSize = cache->itemSize() / itemScaler; if (itemSize == 0.0f) @@ -89,12 +94,19 @@ void ScatterObjectBufferHelper::fullLoad(ScatterSeriesRenderCache *cache, qreal buffered_indices.resize(indicesCount * renderArraySize); buffered_vertices.resize(verticeCount * renderArraySize); - buffered_uvs.resize(uvsCount * renderArraySize); buffered_normals.resize(normalsCount * renderArraySize); + buffered_uvs.resize(uvsCount * renderArraySize); uint pos = 0; + if (cache->colorStyle() == Q3DTheme::ColorStyleRangeGradient) + createRangeGradientUVs(cache, buffered_uvs); + else if (cache->colorStyle() == Q3DTheme::ColorStyleObjectGradient) + createObjectGradientUVs(cache, buffered_uvs, indexed_vertices); + + QVector2D dummyUV(0.0f, 0.0f); + for (uint i = 0; i < renderArraySize; i++) { - ScatterRenderItem &item = renderArray[i]; + const ScatterRenderItem &item = renderArray.at(i); if (!item.isVisible()) { itemCount--; continue; @@ -119,9 +131,11 @@ void ScatterObjectBufferHelper::fullLoad(ScatterSeriesRenderCache *cache, qreal for (int j = 0; j < normalsCount; j++) buffered_normals[j + offset] = indexed_normals[j]; - offset = pos * uvsCount; - for (int j = 0; j < uvsCount; j++) - buffered_uvs[j + offset] = indexed_uvs[j]; + if (cache->colorStyle() == Q3DTheme::ColorStyleUniform) { + offset = pos * uvsCount; + for (int j = 0; j < uvsCount; j++) + buffered_uvs[j + offset] = dummyUV; + } int offsetVertice = i * verticeCount; offset = pos * indicesCount; @@ -164,12 +178,93 @@ void ScatterObjectBufferHelper::fullLoad(ScatterSeriesRenderCache *cache, qreal } } +void ScatterObjectBufferHelper::updateUVs(ScatterSeriesRenderCache *cache) +{ + ObjectHelper *dotObj = cache->object(); + const int uvsCount = dotObj->indexedUVs().count(); + const ScatterRenderItemArray &renderArray = cache->renderArray(); + const uint renderArraySize = renderArray.size(); + QVector buffered_uvs; + buffered_uvs.resize(uvsCount * renderArraySize); + + uint itemCount; + if (cache->colorStyle() == Q3DTheme::ColorStyleRangeGradient) { + itemCount = createRangeGradientUVs(cache, buffered_uvs); + } else if (cache->colorStyle() == Q3DTheme::ColorStyleObjectGradient) { + const QVector indexed_vertices = dotObj->indexedvertices(); + itemCount = createObjectGradientUVs(cache, buffered_uvs, indexed_vertices); + } + + glBindBuffer(GL_ARRAY_BUFFER, m_uvbuffer); + glBufferData(GL_ARRAY_BUFFER, uvsCount * itemCount * sizeof(QVector2D), + &buffered_uvs.at(0), GL_STATIC_DRAW); + + glBindBuffer(GL_ARRAY_BUFFER, 0); +} + +uint ScatterObjectBufferHelper::createRangeGradientUVs(ScatterSeriesRenderCache *cache, + QVector &buffered_uvs) +{ + ObjectHelper *dotObj = cache->object(); + const int uvsCount = dotObj->indexedUVs().count(); + const ScatterRenderItemArray &renderArray = cache->renderArray(); + const uint renderArraySize = renderArray.size(); + + QVector2D uv; + uv.setX(0.0f); + uint pos = 0; + for (uint i = 0; i < renderArraySize; i++) { + const ScatterRenderItem &item = renderArray.at(i); + if (!item.isVisible()) + continue; + + float y = ((item.translation().y() + m_scaleY) * 0.5f ) / m_scaleY; + uv.setY(y); + int offset = pos * uvsCount; + for (int j = 0; j < uvsCount; j++) + buffered_uvs[j + offset] = uv; + + pos++; + } + + return pos; +} + +uint ScatterObjectBufferHelper::createObjectGradientUVs(ScatterSeriesRenderCache *cache, + QVector &buffered_uvs, + const QVector &indexed_vertices) +{ + ObjectHelper *dotObj = cache->object(); + const int uvsCount = dotObj->indexedUVs().count(); + const ScatterRenderItemArray &renderArray = cache->renderArray(); + const uint renderArraySize = renderArray.size(); + + QVector2D uv; + uv.setX(0.0f); + uint pos = 0; + for (uint i = 0; i < renderArraySize; i++) { + const ScatterRenderItem &item = renderArray.at(i); + if (!item.isVisible()) + continue; + + int offset = pos * uvsCount; + for (int j = 0; j < uvsCount; j++) { + uv.setY((indexed_vertices.at(j).y() + 1.0f) / 2.0f); + buffered_uvs[j + offset] = uv; + } + + pos++; + } + + return pos; +} + void ScatterObjectBufferHelper::update(ScatterSeriesRenderCache *cache, qreal dotScale) { initializeOpenGLFunctions(); ObjectHelper *dotObj = cache->object(); - ScatterRenderItemArray &renderArray = cache->renderArray(); + const ScatterRenderItemArray &renderArray = cache->renderArray(); const int renderArraySize = renderArray.size(); QQuaternion seriesRotation(cache->meshRotation()); @@ -198,7 +293,7 @@ void ScatterObjectBufferHelper::update(ScatterSeriesRenderCache *cache, qreal do buffered_vertices.resize(verticeCount * renderArraySize); for (int i = 0; i < renderArraySize; i++) { - ScatterRenderItem &item = renderArray[i]; + const ScatterRenderItem &item = renderArray.at(i); if (!item.isVisible()) continue; diff --git a/src/datavisualization/utils/scatterobjectbufferhelper_p.h b/src/datavisualization/utils/scatterobjectbufferhelper_p.h index 952c3d7d..c45febd1 100644 --- a/src/datavisualization/utils/scatterobjectbufferhelper_p.h +++ b/src/datavisualization/utils/scatterobjectbufferhelper_p.h @@ -43,6 +43,17 @@ public: void fullLoad(ScatterSeriesRenderCache *cache, qreal dotScale); void update(ScatterSeriesRenderCache *cache, qreal dotScale); + void updateUVs(ScatterSeriesRenderCache *cache); + void setScaleY(float scale) { m_scaleY = scale; } + +private: + uint createRangeGradientUVs(ScatterSeriesRenderCache *cache, + QVector &buffered_uvs); + uint createObjectGradientUVs(ScatterSeriesRenderCache *cache, + QVector &buffered_uvs, + const QVector &indexed_vertices); + + float m_scaleY; }; QT_END_NAMESPACE_DATAVISUALIZATION -- cgit v1.2.3 From 7b61b89e4e6a91e7984cd69180468640fe3f9dc8 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Tue, 19 Aug 2014 11:48:32 +0300 Subject: Improve volumetric example MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Changing color table - Different texture details (generation in background) - Optional fps display Change-Id: Iad6b50ef8541084e0142de0f9cb7ae70f841dde9 Reviewed-by: Tomi Korpipää --- examples/datavisualization/volumetric/main.cpp | 48 ++++- .../datavisualization/volumetric/volumetric.cpp | 223 ++++++++++++++++----- examples/datavisualization/volumetric/volumetric.h | 30 ++- src/datavisualization/data/qcustom3dvolume.cpp | 4 +- 4 files changed, 251 insertions(+), 54 deletions(-) diff --git a/examples/datavisualization/volumetric/main.cpp b/examples/datavisualization/volumetric/main.cpp index 7e4c9973..66f62779 100644 --- a/examples/datavisualization/volumetric/main.cpp +++ b/examples/datavisualization/volumetric/main.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include int main(int argc, char **argv) @@ -74,9 +75,41 @@ int main(int argc, char **argv) sliceZSlider->setValue(512); sliceZSlider->setEnabled(true); - QLabel *fpsLabel = new QLabel(QStringLiteral("Fps: "), widget); + QCheckBox *fpsCheckBox = new QCheckBox(widget); + fpsCheckBox->setText(QStringLiteral("Show FPS")); + fpsCheckBox->setChecked(false); + QLabel *fpsLabel = new QLabel(QStringLiteral(""), widget); + QGroupBox *textureDetailGroupBox = new QGroupBox(QStringLiteral("Texture detail")); + + QRadioButton *lowDetailRB = new QRadioButton(widget); + lowDetailRB->setText(QStringLiteral("Low (128x64x128)")); + lowDetailRB->setChecked(true); + + QRadioButton *mediumDetailRB = new QRadioButton(widget); + mediumDetailRB->setText(QStringLiteral("Generating...")); + mediumDetailRB->setChecked(false); + mediumDetailRB->setEnabled(false); + + QRadioButton *highDetailRB = new QRadioButton(widget); + highDetailRB->setText(QStringLiteral("Generating...")); + highDetailRB->setChecked(false); + highDetailRB->setEnabled(false); + + QVBoxLayout *textureDetailVBox = new QVBoxLayout; + textureDetailVBox->addWidget(lowDetailRB); + textureDetailVBox->addWidget(mediumDetailRB); + textureDetailVBox->addWidget(highDetailRB); + textureDetailGroupBox->setLayout(textureDetailVBox); + + QCheckBox *colorTableCheckBox = new QCheckBox(widget); + colorTableCheckBox->setText(QStringLiteral("Alternate color table")); + colorTableCheckBox->setChecked(false); + + vLayout->addWidget(fpsCheckBox); vLayout->addWidget(fpsLabel); + vLayout->addWidget(textureDetailGroupBox); + vLayout->addWidget(colorTableCheckBox); vLayout->addWidget(sliceXCheckBox); vLayout->addWidget(sliceXSlider); vLayout->addWidget(sliceYCheckBox); @@ -86,7 +119,12 @@ int main(int argc, char **argv) VolumetricModifier *modifier = new VolumetricModifier(graph); modifier->setFpsLabel(fpsLabel); + modifier->setMediumDetailRB(mediumDetailRB); + modifier->setHighDetailRB(highDetailRB); + modifier->setSliceSliders(sliceXSlider, sliceYSlider, sliceZSlider); + QObject::connect(fpsCheckBox, &QCheckBox::stateChanged, modifier, + &VolumetricModifier::setFpsMeasurement); QObject::connect(sliceXCheckBox, &QCheckBox::stateChanged, modifier, &VolumetricModifier::sliceX); QObject::connect(sliceYCheckBox, &QCheckBox::stateChanged, modifier, @@ -99,6 +137,14 @@ int main(int argc, char **argv) &VolumetricModifier::adjustSliceY); QObject::connect(sliceZSlider, &QSlider::valueChanged, modifier, &VolumetricModifier::adjustSliceZ); + QObject::connect(lowDetailRB, &QRadioButton::toggled, modifier, + &VolumetricModifier::toggleLowDetail); + QObject::connect(mediumDetailRB, &QRadioButton::toggled, modifier, + &VolumetricModifier::toggleMediumDetail); + QObject::connect(highDetailRB, &QRadioButton::toggled, modifier, + &VolumetricModifier::toggleHighDetail); + QObject::connect(colorTableCheckBox, &QCheckBox::stateChanged, modifier, + &VolumetricModifier::changeColorTable); widget->show(); return app.exec(); diff --git a/examples/datavisualization/volumetric/volumetric.cpp b/examples/datavisualization/volumetric/volumetric.cpp index 0c56374e..ab68e64e 100644 --- a/examples/datavisualization/volumetric/volumetric.cpp +++ b/examples/datavisualization/volumetric/volumetric.cpp @@ -23,20 +23,35 @@ #include #include #include -#include #include +#include +#include #include using namespace QtDataVisualization; -const int textureSize = 256; +const int lowDetailSize(128); +const int mediumDetailSize(256); +const int highDetailSize(512); +const int colorTableSize(256); +const int cutOffColorIndex(15); VolumetricModifier::VolumetricModifier(Q3DScatter *scatter) : m_graph(scatter), m_volumeItem(0), - m_sliceIndexX(textureSize / 2), - m_sliceIndexY(textureSize / 4), - m_sliceIndexZ(textureSize / 2) + m_sliceIndexX(lowDetailSize / 2), + m_sliceIndexY(lowDetailSize / 4), + m_sliceIndexZ(lowDetailSize / 2), + m_mediumDetailRB(0), + m_highDetailRB(0), + m_lowDetailData(0), + m_mediumDetailData(0), + m_highDetailData(0), + m_mediumDetailIndex(0), + m_highDetailIndex(0), + m_sliceSliderX(0), + m_sliceSliderY(0), + m_sliceSliderZ(0) { m_graph->activeTheme()->setType(Q3DTheme::ThemeQt); m_graph->setShadowQuality(QAbstract3DGraph::ShadowQualityNone); @@ -44,8 +59,56 @@ VolumetricModifier::VolumetricModifier(Q3DScatter *scatter) m_graph->setOrthoProjection(true); #if !defined(QT_OPENGL_ES_2) - createVolume(); + m_lowDetailData = new QVector(lowDetailSize * lowDetailSize * lowDetailSize / 2); + m_mediumDetailData = new QVector(mediumDetailSize * mediumDetailSize * mediumDetailSize / 2); + m_highDetailData = new QVector(highDetailSize * highDetailSize * highDetailSize / 2); + + createVolume(lowDetailSize, 0, lowDetailSize, m_lowDetailData); + + m_volumeItem = new QCustom3DVolume; + m_volumeItem->setScaling(QVector3D(2.0f, 1.0f, 2.0f)); + m_volumeItem->setTextureWidth(lowDetailSize); + m_volumeItem->setTextureHeight(lowDetailSize / 2); + m_volumeItem->setTextureDepth(lowDetailSize); + m_volumeItem->setTextureFormat(QImage::Format_Indexed8); + m_volumeItem->setTextureData(new QVector(*m_lowDetailData)); + + // Generate color tables. + // Both tables have a fully transparent colors to fill outer portions of the volume. + + // The primary color table. + // The first visible layer, red, is somewhat transparent. Rest of to colors are opaque. + m_colorTable1.resize(colorTableSize); + m_colorTable2.resize(colorTableSize); + + for (int i = 1; i < colorTableSize; i++) { + if (i < cutOffColorIndex) + m_colorTable1[i] = qRgba(0, 0, 0, 0); + else if (i < 60) + m_colorTable1[i] = qRgba((i * 2) + 120, 0, 0, 50); + else if (i < 120) + m_colorTable1[i] = qRgba(0, ((i - 60) * 2) + 120, 0, 255); + else if (i < 180) + m_colorTable1[i] = qRgba(0, 0, ((i - 120) * 2) + 120, 255); + else + m_colorTable1[i] = qRgba(i, i, i, 255); + } + + // The alternate color table. + // The first visible layer is a thin yellow one, and rest of the volume uses a smooth gradient. + for (int i = 1; i < colorTableSize; i++) { + if (i < cutOffColorIndex) + m_colorTable2[i] = qRgba(0, 0, 0, 0); + else if (i < cutOffColorIndex + 4) + m_colorTable2[i] = qRgba(255, 255, 0, 255); + else + m_colorTable2[i] = qRgba(i, 0, 255 - i, 255); + } + + m_volumeItem->setColorTable(m_colorTable1); + m_graph->addCustomItem(m_volumeItem); + m_timer.start(0); #else // OpenGL ES2 doesn't support 3D textures, so show a warning label instead QCustom3DLabel *warningLabel = new QCustom3DLabel( @@ -59,12 +122,13 @@ VolumetricModifier::VolumetricModifier(Q3DScatter *scatter) m_graph->addCustomItem(warningLabel); m_graph->activeTheme()->setLightStrength(1.0f); #endif - m_graph->setMeasureFps(true); QObject::connect(m_graph->scene()->activeCamera(), &Q3DCamera::zoomLevelChanged, this, &VolumetricModifier::handleZoomLevelChange); QObject::connect(m_graph, &QAbstract3DGraph::currentFpsChanged, this, &VolumetricModifier::handleFpsChange); + QObject::connect(&m_timer, &QTimer::timeout, this, + &VolumetricModifier::handleTimeout); } @@ -78,6 +142,16 @@ void VolumetricModifier::setFpsLabel(QLabel *fpsLabel) m_fpsLabel = fpsLabel; } +void VolumetricModifier::setMediumDetailRB(QRadioButton *button) +{ + m_mediumDetailRB = button; +} + +void VolumetricModifier::setHighDetailRB(QRadioButton *button) +{ + m_highDetailRB = button; +} + void VolumetricModifier::sliceX(int enabled) { if (m_volumeItem) @@ -98,21 +172,21 @@ void VolumetricModifier::sliceZ(int enabled) void VolumetricModifier::adjustSliceX(int value) { - m_sliceIndexX = value / (1024 / textureSize); + m_sliceIndexX = value / (1024 / m_volumeItem->textureWidth()); if (m_volumeItem && m_volumeItem->sliceIndexX() != -1) m_volumeItem->setSliceIndexX(m_sliceIndexX); } void VolumetricModifier::adjustSliceY(int value) { - m_sliceIndexY = value / (1024 / textureSize * 2); + m_sliceIndexY = value / (1024 / m_volumeItem->textureHeight()); if (m_volumeItem && m_volumeItem->sliceIndexY() != -1) m_volumeItem->setSliceIndexY(m_sliceIndexY); } void VolumetricModifier::adjustSliceZ(int value) { - m_sliceIndexZ = value / (1024 / textureSize); + m_sliceIndexZ = value / (1024 / m_volumeItem->textureDepth()); if (m_volumeItem && m_volumeItem->sliceIndexZ() != -1) m_volumeItem->setSliceIndexZ(m_sliceIndexZ); } @@ -126,64 +200,113 @@ void VolumetricModifier::handleZoomLevelChange() void VolumetricModifier::handleFpsChange(qreal fps) { - const QString fpsFormat = QStringLiteral("Fps: %1"); + const QString fpsFormat = QStringLiteral("FPS: %1"); int fps10 = int(fps * 10.0); - m_fpsLabel->setText(fpsFormat.arg(QString::number(qreal(fps10) / 10.0))); + m_fpsLabel->setText(fpsFormat.arg(qreal(fps10) / 10.0)); } -void VolumetricModifier::createVolume() +void VolumetricModifier::handleTimeout() { - m_volumeItem = new QCustom3DVolume; - m_volumeItem->setScaling(QVector3D(2.0f, 1.0f, 2.0f)); - m_volumeItem->setTextureWidth(textureSize); - m_volumeItem->setTextureHeight(textureSize / 2); - m_volumeItem->setTextureDepth(textureSize); - m_volumeItem->setTextureFormat(QImage::Format_Indexed8); - - // Generate color table. First color is fully transparent used to fill outer - // portions of the volume. The second, red layer, is somewhat transparent. - // Rest of to colors are opaque. - QVector colors; - colors.resize(256); - - colors[0] = qRgba(0, 0, 0, 0); - for (int i = 1; i < 256; i++) { - if (i < 60) { - colors[i] = qRgba((i * 2) + 120, 0, 0, 100); - } else if (i < 120) { - colors[i] = qRgba(0, ((i - 60) * 2) + 120, 0, 255); - } else if (i < 180) { - colors[i] = qRgba(0, 0, ((i - 120) * 2) + 120, 255); - } else { - colors[i] = qRgba(i, i, i, 255); + if (m_mediumDetailIndex < mediumDetailSize) { + m_mediumDetailIndex = createVolume(mediumDetailSize, m_mediumDetailIndex, 4, + m_mediumDetailData); + if (m_mediumDetailIndex == mediumDetailSize) { + m_mediumDetailRB->setEnabled(true); + QString label = QStringLiteral("Medium (%1x%2x%1)"); + m_mediumDetailRB->setText(label.arg(mediumDetailSize).arg(mediumDetailSize / 2)); } + } else if (m_highDetailIndex < highDetailSize) { + m_highDetailIndex = createVolume(highDetailSize, m_highDetailIndex, 1, + m_highDetailData); + if (m_highDetailIndex == highDetailSize) { + m_highDetailRB->setEnabled(true); + QString label = QStringLiteral("High (%1x%2x%1)"); + m_highDetailRB->setText(label.arg(highDetailSize).arg(highDetailSize / 2)); + m_timer.stop(); + } + } +} + +void VolumetricModifier::toggleLowDetail(bool enabled) +{ + if (enabled) { + m_volumeItem->setTextureData(new QVector(*m_lowDetailData)); + m_volumeItem->setTextureDimensions(lowDetailSize, lowDetailSize / 2, lowDetailSize); + adjustSliceX(m_sliceSliderX->value()); + adjustSliceY(m_sliceSliderY->value()); + adjustSliceZ(m_sliceSliderZ->value()); } - m_volumeItem->setColorTable(colors); +} - // Generate texture data for an half-ellipsoid. - // This can take a while if the dimensions are large. - // Note that in real world cases, the texture data is usually be supplied - // as a stack of slice images. +void VolumetricModifier::toggleMediumDetail(bool enabled) +{ + if (enabled) { + m_volumeItem->setTextureData(new QVector(*m_mediumDetailData)); + m_volumeItem->setTextureDimensions(mediumDetailSize, mediumDetailSize / 2, mediumDetailSize); + adjustSliceX(m_sliceSliderX->value()); + adjustSliceY(m_sliceSliderY->value()); + adjustSliceZ(m_sliceSliderZ->value()); + } +} - QVector *textureData = new QVector(textureSize * textureSize * textureSize / 2); +void VolumetricModifier::toggleHighDetail(bool enabled) +{ + if (enabled) { + m_volumeItem->setTextureData(new QVector(*m_highDetailData)); + m_volumeItem->setTextureDimensions(highDetailSize, highDetailSize / 2, highDetailSize); + adjustSliceX(m_sliceSliderX->value()); + adjustSliceY(m_sliceSliderY->value()); + adjustSliceZ(m_sliceSliderZ->value()); + } +} + +void VolumetricModifier::setFpsMeasurement(bool enabled) +{ + m_graph->setMeasureFps(enabled); + if (enabled) + m_fpsLabel->setText(QStringLiteral("Measuring...")); + else + m_fpsLabel->setText(QString()); +} + +void VolumetricModifier::setSliceSliders(QSlider *sliderX, QSlider *sliderY, QSlider *sliderZ) +{ + m_sliceSliderX = sliderX; + m_sliceSliderY = sliderY; + m_sliceSliderZ = sliderZ; +} + +void VolumetricModifier::changeColorTable(int enabled) +{ + if (enabled) + m_volumeItem->setColorTable(m_colorTable2); + else + m_volumeItem->setColorTable(m_colorTable1); +} + +int VolumetricModifier::createVolume(int textureSize, int startIndex, int count, + QVector *textureData) +{ + // Generate example texture data for an half-ellipsoid with a section missing. + // This can take a while if the dimensions are large, so we support incremental data generation. QVector3D midPoint(float(textureSize) / 2.0f, float(textureSize) / 2.0f, float(textureSize) / 2.0f); - int index = 0; - for (int i = 0; i < textureSize; i++) { + int index = startIndex * textureSize * textureSize / 2.0f; + int endIndex = startIndex + count; + if (endIndex > textureSize) + endIndex = textureSize; + for (int i = startIndex; i < endIndex; i++) { for (int j = 0; j < textureSize / 2; j++) { for (int k = 0; k < textureSize; k++) { int colorIndex = 0; - // Take a slice out of the ellipsoid + // Take a section out of the ellipsoid if (i >= textureSize / 2 || j >= textureSize / 4 || k >= textureSize / 2) { QVector3D distVec = QVector3D(float(k), float(j * 2), float(i)) - midPoint; float adjLen = qMin(255.0f, (distVec.length() * 512.0f / float(textureSize))); - if (adjLen < 230) - colorIndex = 255 - int(adjLen); - else - colorIndex = 0; + colorIndex = 255 - int(adjLen); } (*textureData)[index] = colorIndex; @@ -192,6 +315,6 @@ void VolumetricModifier::createVolume() } } - m_volumeItem->setTextureData(textureData); + return endIndex; } diff --git a/examples/datavisualization/volumetric/volumetric.h b/examples/datavisualization/volumetric/volumetric.h index 722d4b48..bb7fb3fe 100644 --- a/examples/datavisualization/volumetric/volumetric.h +++ b/examples/datavisualization/volumetric/volumetric.h @@ -21,8 +21,12 @@ #include #include +#include +#include class QLabel; +class QRadioButton; +class QSlider; using namespace QtDataVisualization; @@ -34,6 +38,8 @@ public: ~VolumetricModifier(); void setFpsLabel(QLabel *fpsLabel); + void setMediumDetailRB(QRadioButton *button); + void setHighDetailRB(QRadioButton *button); public slots: void sliceX(int enabled); @@ -44,9 +50,18 @@ public slots: void adjustSliceZ(int value); void handleZoomLevelChange(); void handleFpsChange(qreal fps); + void handleTimeout(); + void toggleLowDetail(bool enabled); + void toggleMediumDetail(bool enabled); + void toggleHighDetail(bool enabled); + void setFpsMeasurement(bool enabled); + void setSliceSliders(QSlider *sliderX, QSlider *sliderY, QSlider *sliderZ); + void changeColorTable(int enabled); private: - void createVolume(); + + int createVolume(int textureSize, int startIndex, int count, + QVector *textureData); Q3DScatter *m_graph; QCustom3DVolume *m_volumeItem; @@ -54,6 +69,19 @@ private: int m_sliceIndexY; int m_sliceIndexZ; QLabel *m_fpsLabel; + QRadioButton *m_mediumDetailRB; + QRadioButton *m_highDetailRB; + QVector *m_lowDetailData; + QVector *m_mediumDetailData; + QVector *m_highDetailData; + QTimer m_timer; + int m_mediumDetailIndex; + int m_highDetailIndex; + QSlider *m_sliceSliderX; + QSlider *m_sliceSliderY; + QSlider *m_sliceSliderZ; + QVector m_colorTable1; + QVector m_colorTable2; }; #endif diff --git a/src/datavisualization/data/qcustom3dvolume.cpp b/src/datavisualization/data/qcustom3dvolume.cpp index 766f1589..a7bbae9b 100644 --- a/src/datavisualization/data/qcustom3dvolume.cpp +++ b/src/datavisualization/data/qcustom3dvolume.cpp @@ -239,8 +239,8 @@ int QCustom3DVolume::textureDepth() const void QCustom3DVolume::setTextureDimensions(int width, int height, int depth) { setTextureWidth(width); - setTextureWidth(height); - setTextureWidth(depth); + setTextureHeight(height); + setTextureDepth(depth); } /*! -- cgit v1.2.3 From 7ef676c7cbe5e3a960a66150794be3a862642073 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Tue, 19 Aug 2014 14:15:52 +0300 Subject: Make volume transparency uniform regardless of viewing angle MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: If3acbb161b99f67d81f7af9c05ae8bcfe154e052 Reviewed-by: Tomi Korpipää --- examples/datavisualization/volumetric/volumetric.cpp | 2 +- src/datavisualization/engine/shaders/texture3d.frag | 11 +++++++++-- src/datavisualization/engine/shaders/texture3dslice.frag | 3 ++- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/examples/datavisualization/volumetric/volumetric.cpp b/examples/datavisualization/volumetric/volumetric.cpp index ab68e64e..b4ea769c 100644 --- a/examples/datavisualization/volumetric/volumetric.cpp +++ b/examples/datavisualization/volumetric/volumetric.cpp @@ -85,7 +85,7 @@ VolumetricModifier::VolumetricModifier(Q3DScatter *scatter) if (i < cutOffColorIndex) m_colorTable1[i] = qRgba(0, 0, 0, 0); else if (i < 60) - m_colorTable1[i] = qRgba((i * 2) + 120, 0, 0, 50); + m_colorTable1[i] = qRgba((i * 2) + 120, 0, 0, 20); else if (i < 120) m_colorTable1[i] = qRgba(0, ((i - 60) * 2) + 120, 0, 255); else if (i < 180) diff --git a/src/datavisualization/engine/shaders/texture3d.frag b/src/datavisualization/engine/shaders/texture3d.frag index b9d87fcc..e56506ff 100644 --- a/src/datavisualization/engine/shaders/texture3d.frag +++ b/src/datavisualization/engine/shaders/texture3d.frag @@ -9,7 +9,7 @@ uniform highp int color8Bit; uniform highp vec3 textureDimensions; uniform highp int sampleCount; // This is the maximum sample count -const float alphaThreshold = 0.001; +const highp float alphaThreshold = 0.0001; void main() { // Raytrace into volume, need to sample pixels along the eye ray until we hit opacity 1 @@ -51,6 +51,10 @@ void main() { // Offset a fraction of a step so we are not exactly on a texel boundary. highp vec3 curPos = rayStart - (0.5 * step); + // Adjust alpha multiplier according to the step size to get uniform alpha effect + // regardless of the ray angle. + highp float alphaMultiplier = stepSize / (1.0 / sampleCount); + highp float totalDist = 0.0; highp float totalAlpha = 0.0; highp vec4 destColor = vec4(0, 0, 0, 0); @@ -63,7 +67,10 @@ void main() { if (color8Bit != 0) curColor = colorIndex[int(curColor.r * 255.0)]; - curAlpha = curColor.a; + if (curColor.a == 1.0) + curAlpha = 1.0; + else + curAlpha = clamp(curColor.a * alphaMultiplier, 0.0, 1.0); if (curAlpha > alphaThreshold) { curRgb = curColor.rgb * curAlpha * (1.0 - totalAlpha); destColor.rgb += curRgb; diff --git a/src/datavisualization/engine/shaders/texture3dslice.frag b/src/datavisualization/engine/shaders/texture3dslice.frag index 641b32a5..e095bc5f 100644 --- a/src/datavisualization/engine/shaders/texture3dslice.frag +++ b/src/datavisualization/engine/shaders/texture3dslice.frag @@ -12,7 +12,8 @@ const highp vec3 xPlaneNormal = vec3(1.0, 0, 0); const highp vec3 yPlaneNormal = vec3(0, 1.0, 0); const highp vec3 zPlaneNormal = vec3(0, 0, 1.0); -const float alphaThreshold = 0.001; +const highp float alphaThreshold = 0.0001; + void main() { // Raytrace into volume, need to sample pixels along the eye ray until we hit opacity 1 -- cgit v1.2.3 From 8fab0a9cfcbed9deb47f4a9bd101434985c1c611 Mon Sep 17 00:00:00 2001 From: Mika Salmela Date: Thu, 21 Aug 2014 09:55:23 +0300 Subject: Gradient to highlight series MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added a gradient to highlight series on textured surface example. Change-Id: I2fc17dfa79f986cf5b651d964eb3bcf5d65f1c80 Reviewed-by: Tomi Korpipää --- .../texturesurface/custominputhandler.cpp | 2 - .../texturesurface/custominputhandler.h | 6 +-- .../texturesurface/doc/src/texturesurface.qdoc | 53 +++++++++++++++------- .../texturesurface/highlightseries.cpp | 28 +++++++++++- .../texturesurface/highlightseries.h | 3 ++ examples/datavisualization/texturesurface/main.cpp | 37 +++++++++++++++ .../texturesurface/surfacegraph.cpp | 11 +++-- .../texturesurface/topographicseries.cpp | 2 - 8 files changed, 114 insertions(+), 28 deletions(-) diff --git a/examples/datavisualization/texturesurface/custominputhandler.cpp b/examples/datavisualization/texturesurface/custominputhandler.cpp index 0f79feef..2510df54 100644 --- a/examples/datavisualization/texturesurface/custominputhandler.cpp +++ b/examples/datavisualization/texturesurface/custominputhandler.cpp @@ -21,8 +21,6 @@ #include #include -#include - CustomInputHandler::CustomInputHandler(QAbstract3DGraph *graph, QObject *parent) : Q3DInputHandler(parent), m_highlight(0), diff --git a/examples/datavisualization/texturesurface/custominputhandler.h b/examples/datavisualization/texturesurface/custominputhandler.h index 5307a6be..8bef990e 100644 --- a/examples/datavisualization/texturesurface/custominputhandler.h +++ b/examples/datavisualization/texturesurface/custominputhandler.h @@ -40,15 +40,15 @@ class CustomInputHandler : public Q3DInputHandler public: explicit CustomInputHandler(QAbstract3DGraph *graph, QObject *parent = 0); - inline void setLimits(float min, float max) { + inline void setLimits(float min, float max, float minRange) { m_areaMinValue = min; m_areaMaxValue = max; m_axisXMinValue = m_areaMinValue; m_axisXMaxValue = m_areaMaxValue; m_axisZMinValue = m_areaMinValue; m_axisZMaxValue = m_areaMaxValue; - m_axisXMinRange = (m_areaMaxValue - m_areaMinValue) * 0.49f; - m_axisZMinRange = (m_areaMaxValue - m_areaMinValue) * 0.49f; + m_axisXMinRange = minRange; + m_axisZMinRange = minRange; } inline void setAxes(QValue3DAxis *axisX, QValue3DAxis *axisY, QValue3DAxis *axisZ) { m_axisX = axisX; diff --git a/examples/datavisualization/texturesurface/doc/src/texturesurface.qdoc b/examples/datavisualization/texturesurface/doc/src/texturesurface.qdoc index a4aa219e..483b8110 100644 --- a/examples/datavisualization/texturesurface/doc/src/texturesurface.qdoc +++ b/examples/datavisualization/texturesurface/doc/src/texturesurface.qdoc @@ -21,14 +21,15 @@ \title Textured Surface Example \ingroup qtdatavisualization_examples \brief Using texture with Q3DSurface. + \since QtDataVisualization 1.2 The textured surface example shows how to add an image as a texture for a surface. The example shows also how to: \list \li Create a surface series from an image - \li Highlight an area of the surface \li Use custom input handler to enable zooming and panning + \li Highlight an area of the surface \endlist \image texturesurface-example.png @@ -64,6 +65,28 @@ And then the actual decoding. \snippet texturesurface/topographicseries.cpp 1 + \section1 Use custom input handler to enable zooming and panning + + For the panning the implementation is similar to the \l{Axis Range Dragging With Labels Example}. + The difference is that in this example we follow only dragging of X and Z axis and we don't + allow dragging the surface outside the graph. The control for this is very simple and done as + on the following example for the X axis. + + \snippet texturesurface/custominputhandler.cpp 0 + + For the zooming we catch the \c wheelEvent and adjust the X and Y axis ranges according to delta + value on QWheelEvent. The Y axis is also adjusted so that the aspect ratio between Y axis and + XZ plane stays the same, and we don't get silly looking graph with height exaggerated too much. + + \snippet texturesurface/custominputhandler.cpp 1 + + In this case we want to control the zoom level so that it won't get too near to or far from the + surface. For instance, if the value for the X axis gets below the allowed, i.e. zooming gets too + far, the value is set to the minimum allowed value. If the range is going to below the range + minimum, both ends of the axis are adjusted so that the range stays at the limit. + + \snippet texturesurface/custominputhandler.cpp 2 + \section1 Highlight an area of the surface The main idea on creating a highlight on the surface is to create a copy of the series and add @@ -81,25 +104,23 @@ \snippet texturesurface/highlightseries.cpp 1 - \section1 Use custom input handler to enable zooming and panning + \section1 A gradient to the highlight series - For the panning the implementation is similar to the \l{Axis Range Dragging With Labels Example}. - The difference is that in this example we follow only dragging of X and Z axis and we don't - allow dragging the surface outside the graph. The control for this is very simple and done as - on the following example for the X axis. + Since the \c HighlightSeries is QSurface3DSeries, we can use all the decoration methods series can + have. In this example we added a gradient to emphasize the elevation. Because the suitable gradient + style depends on the range of the Y axis and we change the range when zooming, we need to adjust + the gradient color positions as the range change. - \snippet texturesurface/custominputhandler.cpp 0 + For the gradient color positions we define proportional values. - For the zooming we catch the \c wheelEvent and adjust the X and Y axis ranges according to delta - value on QWheelEvent. The Y axis is also adjusted so that the aspect ratio between Y axis and - XZ plane stays the same, and we don't get silly looking graph with height exaggerated too much. + \snippet texturesurface/highlightseries.cpp 2 - \snippet texturesurface/custominputhandler.cpp 1 + The gradient modification is done on \c handleGradientChange method and we connect it to react to + changes on Y axis. - In this case we want to control the zoom level so that it won't get too near to or far from the - surface. For instance, if the value for the X axis gets below the allowed, i.e. zooming gets too - far, the value is set to the minimum allowed value. If the range is going to below the range - minimum, both ends of the axis are adjusted so that the range stays at the limit. + \snippet texturesurface/surfacegraph.cpp 1 - \snippet texturesurface/custominputhandler.cpp 2 + When a change on Y axis max value happens, we calculate the gradient color positions. + + \snippet texturesurface/highlightseries.cpp 3 */ diff --git a/examples/datavisualization/texturesurface/highlightseries.cpp b/examples/datavisualization/texturesurface/highlightseries.cpp index 7e5f7efb..13d1fba3 100644 --- a/examples/datavisualization/texturesurface/highlightseries.cpp +++ b/examples/datavisualization/texturesurface/highlightseries.cpp @@ -18,10 +18,16 @@ #include "highlightseries.h" -#include - using namespace QtDataVisualization; +//! [2] +const float darkRedPos = 1.0f; +const float redPos = 0.8f; +const float yellowPos = 0.6f; +const float greenPos = 0.4f; +const float darkGreenPos = 0.2f; +//! [2] + HighlightSeries::HighlightSeries() : m_width(100), m_height(100) @@ -93,3 +99,21 @@ void HighlightSeries::handlePositionChange(const QPoint &position) setVisible(true); } //! [1] + +//! [3] +void HighlightSeries::handleGradientChange(float value) +{ + float ratio = m_minHeight / value; + + QLinearGradient gr; + gr.setColorAt(0.0f, Qt::black); + gr.setColorAt(darkGreenPos * ratio, Qt::darkGreen); + gr.setColorAt(greenPos * ratio, Qt::green); + gr.setColorAt(yellowPos * ratio, Qt::yellow); + gr.setColorAt(redPos * ratio, Qt::red); + gr.setColorAt(darkRedPos * ratio, Qt::darkRed); + + setBaseGradient(gr); + setColorStyle(Q3DTheme::ColorStyleRangeGradient); +} +//! [3] diff --git a/examples/datavisualization/texturesurface/highlightseries.h b/examples/datavisualization/texturesurface/highlightseries.h index 36361fbc..aa1590e5 100644 --- a/examples/datavisualization/texturesurface/highlightseries.h +++ b/examples/datavisualization/texturesurface/highlightseries.h @@ -33,9 +33,11 @@ public: ~HighlightSeries(); void setTopographicSeries(TopographicSeries *series); + inline void setMinHeight(float height) { m_minHeight = height; } public slots: void handlePositionChange(const QPoint &position); + void handleGradientChange(float value); private: int m_width; @@ -44,6 +46,7 @@ private: int m_srcHeight; QPoint m_position; TopographicSeries *m_topographicSeries; + float m_minHeight; }; #endif // HIGHLIGHTSERIES_H diff --git a/examples/datavisualization/texturesurface/main.cpp b/examples/datavisualization/texturesurface/main.cpp index e73dc864..ed1a9be4 100644 --- a/examples/datavisualization/texturesurface/main.cpp +++ b/examples/datavisualization/texturesurface/main.cpp @@ -22,8 +22,11 @@ #include #include #include +#include #include +#include #include +#include int main(int argc, char **argv) { @@ -49,7 +52,41 @@ int main(int argc, char **argv) QCheckBox *enableTexture = new QCheckBox(widget); enableTexture->setText(QStringLiteral("Surface texture")); + int height = 400; + int width = 100; + int border = 10; + QLinearGradient gr(0, 0, 1, height - 2 * border); + gr.setColorAt(1.0f, Qt::black); + gr.setColorAt(0.8f, Qt::darkGreen); + gr.setColorAt(0.6f, Qt::green); + gr.setColorAt(0.4f, Qt::yellow); + gr.setColorAt(0.2f, Qt::red); + gr.setColorAt(0.0f, Qt::darkRed); + + QPixmap pm(width, height); + pm.fill(Qt::transparent); + QPainter pmp(&pm); + pmp.setBrush(QBrush(gr)); + pmp.setPen(Qt::NoPen); + pmp.drawRect(border, border, 35, height - 2 * border); + pmp.setPen(Qt::black); + int step = (height - 2 * border) / 5; + for (int i = 0; i < 6; i++) { + int yPos = i * step + border; + pmp.drawLine(border, yPos, 55, yPos); + pmp.drawText(60, yPos + 2, QString("%1 m").arg(550 - (i * 110))); + } + + QLabel *label = new QLabel(widget); + label->setPixmap(pm); + + QGroupBox *heightMapGroupBox = new QGroupBox(QStringLiteral("Height color map")); + QVBoxLayout *colorMapVBox = new QVBoxLayout; + colorMapVBox->addWidget(label); + heightMapGroupBox->setLayout(colorMapVBox); + vLayout->addWidget(enableTexture); + vLayout->addWidget(heightMapGroupBox); widget->show(); diff --git a/examples/datavisualization/texturesurface/surfacegraph.cpp b/examples/datavisualization/texturesurface/surfacegraph.cpp index f85ef5c8..785cf576 100644 --- a/examples/datavisualization/texturesurface/surfacegraph.cpp +++ b/examples/datavisualization/texturesurface/surfacegraph.cpp @@ -22,13 +22,12 @@ #include #include -#include - using namespace QtDataVisualization; const float areaWidth = 8000.0f; const float areaHeight = 8000.0f; const float aspectRatio = 0.1389f; +const float minRange = areaWidth * 0.49f; SurfaceGraph::SurfaceGraph(Q3DSurface *surface) : m_graph(surface) @@ -56,6 +55,12 @@ SurfaceGraph::SurfaceGraph(Q3DSurface *surface) m_highlight = new HighlightSeries(); m_highlight->setTopographicSeries(m_topography); + m_highlight->setMinHeight(minRange * aspectRatio); + m_highlight->handleGradientChange(areaWidth * aspectRatio); +//! [1] + QObject::connect(m_graph->axisY(), &QValue3DAxis::maxChanged, + m_highlight, &HighlightSeries::handleGradientChange); +//! [1] m_graph->addSeries(m_topography); m_graph->addSeries(m_highlight); @@ -63,7 +68,7 @@ SurfaceGraph::SurfaceGraph(Q3DSurface *surface) m_inputHandler = new CustomInputHandler(m_graph); m_inputHandler->setHighlightSeries(m_highlight); m_inputHandler->setAxes(m_graph->axisX(), m_graph->axisY(), m_graph->axisZ()); - m_inputHandler->setLimits(0.0f, areaWidth); + m_inputHandler->setLimits(0.0f, areaWidth, minRange); m_inputHandler->setAspectRatio(aspectRatio); m_graph->setActiveInputHandler(m_inputHandler); diff --git a/examples/datavisualization/texturesurface/topographicseries.cpp b/examples/datavisualization/texturesurface/topographicseries.cpp index a1c61f56..2fa29d35 100644 --- a/examples/datavisualization/texturesurface/topographicseries.cpp +++ b/examples/datavisualization/texturesurface/topographicseries.cpp @@ -18,8 +18,6 @@ #include "topographicseries.h" -#include - using namespace QtDataVisualization; //! [0] -- cgit v1.2.3 From ae411d84b9eac08c217bdda3aa5fbc6f39d03d85 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Fri, 22 Aug 2014 13:19:18 +0300 Subject: Tweak volume shaders. Also fix custom object default texture. Change-Id: Iba9ff6afb807d9f7a4e1f58b0e5fb4bca4c9c431 Reviewed-by: Mika Salmela --- src/datavisualization/data/customrenderitem.cpp | 18 +- src/datavisualization/data/qcustom3ditem.cpp | 2 + .../engine/shaders/texture3d.frag | 136 +++++----- .../engine/shaders/texture3dslice.frag | 27 +- tests/volumetrictest/cubeFilledFlat.obj | 54 ++++ tests/volumetrictest/main.cpp | 3 +- tests/volumetrictest/volumetrictest.cpp | 293 +++++++++++++++++---- tests/volumetrictest/volumetrictest.h | 7 +- tests/volumetrictest/volumetrictest.qrc | 1 + 9 files changed, 413 insertions(+), 128 deletions(-) create mode 100644 tests/volumetrictest/cubeFilledFlat.obj diff --git a/src/datavisualization/data/customrenderitem.cpp b/src/datavisualization/data/customrenderitem.cpp index b96a4957..3eb68845 100644 --- a/src/datavisualization/data/customrenderitem.cpp +++ b/src/datavisualization/data/customrenderitem.cpp @@ -52,13 +52,17 @@ void CustomRenderItem::setMesh(const QString &meshFile) void CustomRenderItem::setColorTable(const QVector &colors) { - m_colorTable.resize(colors.size()); - for (int i = 0; i < m_colorTable.size(); i++) { - const QRgb &rgb = colors.at(i); - m_colorTable[i] = QVector4D(float(qRed(rgb)) / 255.0f, - float(qGreen(rgb)) / 255.0f, - float(qBlue(rgb)) / 255.0f, - float(qAlpha(rgb)) / 255.0f); + m_colorTable.resize(256); + for (int i = 0; i < 256; i++) { + if (i < colors.size()) { + const QRgb &rgb = colors.at(i); + m_colorTable[i] = QVector4D(float(qRed(rgb)) / 255.0f, + float(qGreen(rgb)) / 255.0f, + float(qBlue(rgb)) / 255.0f, + float(qAlpha(rgb)) / 255.0f); + } else { + m_colorTable[i] = QVector4D(0.0f, 0.0f, 0.0f, 0.0f); + } } } diff --git a/src/datavisualization/data/qcustom3ditem.cpp b/src/datavisualization/data/qcustom3ditem.cpp index 1d419cee..cb843b62 100644 --- a/src/datavisualization/data/qcustom3ditem.cpp +++ b/src/datavisualization/data/qcustom3ditem.cpp @@ -367,6 +367,7 @@ QString QCustom3DItem::textureFile() const QCustom3DItemPrivate::QCustom3DItemPrivate(QCustom3DItem *q) : q_ptr(q), + m_textureImage(QImage(1, 1, QImage::Format_ARGB32)), m_position(QVector3D(0.0f, 0.0f, 0.0f)), m_positionAbsolute(false), m_scaling(QVector3D(0.1f, 0.1f, 0.1f)), @@ -382,6 +383,7 @@ QCustom3DItemPrivate::QCustom3DItemPrivate(QCustom3DItem *q, const QString &mesh const QVector3D &position, const QVector3D &scaling, const QQuaternion &rotation) : q_ptr(q), + m_textureImage(QImage(1, 1, QImage::Format_ARGB32)), m_meshFile(meshFile), m_position(position), m_positionAbsolute(false), diff --git a/src/datavisualization/engine/shaders/texture3d.frag b/src/datavisualization/engine/shaders/texture3d.frag index e56506ff..90876596 100644 --- a/src/datavisualization/engine/shaders/texture3d.frag +++ b/src/datavisualization/engine/shaders/texture3d.frag @@ -12,78 +12,90 @@ uniform highp int sampleCount; // This is the maximum sample count const highp float alphaThreshold = 0.0001; void main() { - // Raytrace into volume, need to sample pixels along the eye ray until we hit opacity 1 - - // Find out where ray intersects the object highp vec3 rayDir = -(cameraPositionRelativeToModel - pos); - highp vec3 invRayDir = 1.0 / rayDir; - highp vec3 minCorner = vec3(-1.0); - highp vec3 maxCorner = vec3(1.0); - highp vec3 t1 = invRayDir * (minCorner - pos); - highp vec3 t2 = invRayDir * (maxCorner - pos); - highp vec3 tmin = min(t1, t2); - highp vec3 tmax = max(t1, t2); - highp vec2 t = max(tmin.xx, tmin.yz); - t = min(tmax.xx, tmax.yz); - float tFar = min(t.x, t.y); - highp vec3 rayStart = pos; + vec3 rayStart = pos; // Flip Y and Z so QImage bits work directly for texture and first image is in the front + rayDir.yz = -rayDir.yz; rayStart.yz = -rayStart.yz; - highp vec3 rayStop = pos + rayDir * tFar; - rayStop.yz = -rayStop.yz; - - // Convert intersections to texture coords - rayStart = 0.5 * (rayStart + 1.0); - rayStop = 0.5 * (rayStop + 1.0); - - highp vec3 ray = rayStop - rayStart; - highp float fullDist = length(ray); - highp float rayX = abs(ray.x) * textureDimensions.x; - highp float rayY = abs(ray.y) * textureDimensions.y; - highp float rayZ = abs(ray.z) * textureDimensions.z; - - highp float maxRayDim = max(rayX, rayY); - maxRayDim = max(maxRayDim, rayZ); - - highp vec3 step = ray / maxRayDim; - highp float stepSize = abs(fullDist / maxRayDim); - - // Offset a fraction of a step so we are not exactly on a texel boundary. - highp vec3 curPos = rayStart - (0.5 * step); - // Adjust alpha multiplier according to the step size to get uniform alpha effect - // regardless of the ray angle. - highp float alphaMultiplier = stepSize / (1.0 / sampleCount); + // Calculate ray intersection endpoint + vec3 rayStop; + if (rayDir.x == 0.0) { + rayStop.yz = rayStart.yz; + rayStop.x = -rayStart.x; + } else if (rayDir.y == 0.0) { + rayStop.xz = rayStart.xz; + rayStop.y = -rayStart.y; + } else if (rayDir.z == 0.0) { + rayStop.xy = rayStart.xy; + rayStop.z = -rayStart.z; + } else { + highp vec3 boxBounds = vec3(1.0, 1.0, 1.0); + highp vec3 invRayDir = 1.0 / rayDir; + if (rayDir.x < 0) + boxBounds.x = -1.0; + if (rayDir.y < 0) + boxBounds.y = -1.0; + if (rayDir.z < 0) + boxBounds.z = -1.0; + highp vec3 t = (boxBounds - rayStart) * invRayDir; + highp float minT = min(t.x, t.y); + minT = min(minT, t.z); + rayStop = rayStart + minT * rayDir; + } - highp float totalDist = 0.0; - highp float totalAlpha = 0.0; highp vec4 destColor = vec4(0, 0, 0, 0); - highp vec4 curColor = vec4(0, 0, 0, 0); - highp vec3 curRgb = vec3(0, 0, 0); - highp float curAlpha = 0.0; - - for (int i = 0; i < sampleCount; i++) { - curColor = texture3D(textureSampler, curPos); - if (color8Bit != 0) - curColor = colorIndex[int(curColor.r * 255.0)]; + highp float totalAlpha = 0.0; - if (curColor.a == 1.0) - curAlpha = 1.0; - else - curAlpha = clamp(curColor.a * alphaMultiplier, 0.0, 1.0); - if (curAlpha > alphaThreshold) { - curRgb = curColor.rgb * curAlpha * (1.0 - totalAlpha); - destColor.rgb += curRgb; - totalAlpha += curAlpha; - } - curPos += step; - totalDist += stepSize; - if (totalDist > fullDist || totalAlpha >= 1.0) { - break; + if (rayStart != rayStop) { + // Convert intersections to texture coords + rayStart = 0.5 * (rayStart + 1.0); + rayStop = 0.5 * (rayStop + 1.0); + + highp vec3 ray = rayStop - rayStart; + highp float fullDist = abs(length(ray)); + highp float rayX = abs(ray.x) * textureDimensions.x; + highp float rayY = abs(ray.y) * textureDimensions.y; + highp float rayZ = abs(ray.z) * textureDimensions.z; + highp float maxRayDim = max(rayX, rayY); + maxRayDim = max(maxRayDim, rayZ); + int maxCount = int(floor(maxRayDim)); + + highp vec3 step = ray / maxRayDim; + highp float stepSize = fullDist / maxRayDim; + + rayStart += (step * 0.001); + highp vec3 curPos = rayStart; + + // Adjust alpha multiplier according to the step size to get uniform alpha effect + // regardless of the ray angle. + highp float alphaMultiplier = stepSize / (1.0 / sampleCount); + + highp vec4 curColor = vec4(0, 0, 0, 0); + highp vec3 curRgb = vec3(0, 0, 0); + highp float curAlpha = 0.0; + + // Raytrace into volume, need to sample pixels along the eye ray until we hit opacity 1 + for (int i = 0; i < sampleCount; i++) { + curColor = texture3D(textureSampler, curPos); + if (color8Bit != 0) + curColor = colorIndex[int(curColor.r * 255.0)]; + + if (curColor.a == 1.0) + curAlpha = 1.0; + else + curAlpha = clamp(curColor.a * alphaMultiplier, 0.0, 1.0); + if (curAlpha > alphaThreshold) { + curRgb = curColor.rgb * curAlpha * (1.0 - totalAlpha); + destColor.rgb += curRgb; + totalAlpha += curAlpha; + } + if (i == maxCount || totalAlpha >= 1.0) + break; + curPos += step; } } destColor.a = totalAlpha; gl_FragColor = clamp(destColor, 0.0, 1.0); } - diff --git a/src/datavisualization/engine/shaders/texture3dslice.frag b/src/datavisualization/engine/shaders/texture3dslice.frag index e095bc5f..409ab41d 100644 --- a/src/datavisualization/engine/shaders/texture3dslice.frag +++ b/src/datavisualization/engine/shaders/texture3dslice.frag @@ -15,24 +15,27 @@ const highp vec3 zPlaneNormal = vec3(0, 0, 1.0); const highp float alphaThreshold = 0.0001; void main() { - // Raytrace into volume, need to sample pixels along the eye ray until we hit opacity 1 - // Find out where ray intersects the slice planes highp vec3 rayDir = -(cameraPositionRelativeToModel - pos); + rayDir = normalize(rayDir); highp vec3 rayStart = pos; // Flip Y and Z so QImage bits work directly for texture and first image is in the front rayStart.yz = -rayStart.yz; rayDir.yz = -rayDir.yz; - highp vec3 invRayDir = 1.0 / rayDir; - highp vec3 minCorner = vec3(-1.0); - highp vec3 maxCorner = vec3(1.0); - highp vec3 t1 = invRayDir * (minCorner - rayStart); - highp vec3 t2 = invRayDir * (maxCorner - rayStart); - highp vec3 tmin = min(t1, t2); - highp vec3 tmax = max(t1, t2); - highp vec2 t = max(tmin.xx, tmin.yz); - t = min(tmax.xx, tmax.yz); - float tFar = min(t.x, t.y); + highp float tFar = 2.0f; + if (rayDir.x != 0.0 && rayDir.y != 0.0 && rayDir.z != 0.0) { + highp vec3 boxBounds = vec3(1.0, 1.0, 1.0); + highp vec3 invRayDir = 1.0 / rayDir; + if (rayDir.x < 0) + boxBounds.x = -1.0; + if (rayDir.y < 0) + boxBounds.y = -1.0; + if (rayDir.z < 0) + boxBounds.z = -1.0; + highp vec3 t = (boxBounds - rayStart) * invRayDir; + tFar = max(t.x, t.y); + tFar = max(tFar, t.z); + } highp vec3 xPoint = vec3(volumeSliceIndices.x, 0, 0); highp vec3 yPoint = vec3(0, volumeSliceIndices.y, 0); diff --git a/tests/volumetrictest/cubeFilledFlat.obj b/tests/volumetrictest/cubeFilledFlat.obj new file mode 100644 index 00000000..108cf7ac --- /dev/null +++ b/tests/volumetrictest/cubeFilledFlat.obj @@ -0,0 +1,54 @@ +# Blender v2.66 (sub 0) OBJ File: 'cube_filled.blend' +# www.blender.org +o Cube +v -1.000000 -1.000000 1.000000 +v -1.000000 -1.000000 -1.000000 +v 1.000000 -1.000000 -1.000000 +v 1.000000 -1.000000 1.000000 +v -1.000000 1.000000 1.000000 +v -1.000000 1.000000 -1.000000 +v 1.000000 1.000000 -1.000000 +v 1.000000 1.000000 1.000000 +vt 0.666667 0.332314 +vt 0.334353 0.333333 +vt 0.665647 0.000000 +vt 0.001020 0.333333 +vt 0.000000 0.001020 +vt 0.333333 0.332314 +vt 0.333333 0.665647 +vt 0.001019 0.666667 +vt 0.000000 0.334353 +vt 0.334353 0.666667 +vt 0.333333 0.334353 +vt 0.665647 0.333333 +vt 0.333333 0.667686 +vt 0.665647 0.666667 +vt 0.666667 0.998980 +vt 0.667686 0.333333 +vt 0.666667 0.001019 +vt 0.998980 0.000000 +vt 0.333333 0.001019 +vt 0.332314 0.000000 +vt 0.332314 0.333333 +vt 0.666667 0.665647 +vt 0.334353 1.000000 +vt 1.000000 0.332314 +vn -1.000000 0.000000 0.000000 +vn 0.000000 0.000000 -1.000000 +vn 1.000000 -0.000000 0.000000 +vn 0.000000 0.000000 1.000000 +vn 0.000000 1.000000 0.000000 +vn -0.000000 -1.000000 -0.000000 +s off +f 5/1/1 6/2/1 1/3/1 +f 6/4/2 7/5/2 2/6/2 +f 7/7/3 8/8/3 4/9/3 +f 8/10/4 5/11/4 1/12/4 +f 8/13/5 7/14/5 6/15/5 +f 2/16/6 3/17/6 4/18/6 +f 6/2/1 2/19/1 1/3/1 +f 7/5/2 3/20/2 2/6/2 +f 3/21/3 7/7/3 4/9/3 +f 4/22/4 8/10/4 1/12/4 +f 5/23/5 8/13/5 6/15/5 +f 1/24/6 2/16/6 4/18/6 diff --git a/tests/volumetrictest/main.cpp b/tests/volumetrictest/main.cpp index 46edf576..a15caf88 100644 --- a/tests/volumetrictest/main.cpp +++ b/tests/volumetrictest/main.cpp @@ -35,7 +35,7 @@ int main(int argc, char **argv) QWidget *container = QWidget::createWindowContainer(graph); QSize screenSize = graph->screen()->size(); - container->setMinimumSize(QSize(screenSize.width() / 2, screenSize.height() / 1.5)); + container->setMinimumSize(QSize(screenSize.width() / 4, screenSize.height() / 4)); container->setMaximumSize(screenSize); container->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); container->setFocusPolicy(Qt::StrongFocus); @@ -47,6 +47,7 @@ int main(int argc, char **argv) hLayout->addLayout(vLayout); widget->setWindowTitle(QStringLiteral("Volumetric TEST")); + widget->resize(QSize(screenSize.width() / 1.5, screenSize.height() / 1.5)); QCheckBox *sliceXCheckBox = new QCheckBox(widget); sliceXCheckBox->setText(QStringLiteral("Slice volume on X axis")); diff --git a/tests/volumetrictest/volumetrictest.cpp b/tests/volumetrictest/volumetrictest.cpp index 04aad052..701cfc04 100644 --- a/tests/volumetrictest/volumetrictest.cpp +++ b/tests/volumetrictest/volumetrictest.cpp @@ -34,11 +34,14 @@ const int imageCount = 512; VolumetricModifier::VolumetricModifier(Q3DScatter *scatter) : m_graph(scatter), m_volumeItem(0), + m_volumeItem2(0), + m_volumeItem3(0), m_sliceIndexX(0), m_sliceIndexY(0), m_sliceIndexZ(0) { m_graph->activeTheme()->setType(Q3DTheme::ThemeQt); + //m_graph->activeTheme()->setType(Q3DTheme::ThemeIsabelle); m_graph->setShadowQuality(QAbstract3DGraph::ShadowQualityNone); m_graph->scene()->activeCamera()->setCameraPreset(Q3DCamera::CameraPresetFront); m_graph->setOrthoProjection(true); @@ -46,15 +49,27 @@ VolumetricModifier::VolumetricModifier(Q3DScatter *scatter) createVolume(); createAnotherVolume(); + createYetAnotherVolume(); + + m_plainItem = new QCustom3DItem; + QImage texture(2, 2, QImage::Format_ARGB32); + texture.fill(QColor(200, 200, 200, 130)); + m_plainItem->setMeshFile(QStringLiteral(":/mesh")); + m_plainItem->setTextureImage(texture); + m_plainItem->setRotation(m_volumeItem->rotation()); + m_plainItem->setPosition(m_volumeItem->position() + QVector3D(0.8f, 0.0f, 0.0f)); + m_plainItem->setScaling(m_volumeItem->scaling()); m_graph->addCustomItem(m_volumeItem); m_graph->addCustomItem(m_volumeItem2); - m_graph->setMeasureFps(true); + m_graph->addCustomItem(m_volumeItem3); + m_graph->addCustomItem(m_plainItem); + //m_graph->setMeasureFps(true); - QObject::connect(m_graph->scene()->activeCamera(), &Q3DCamera::zoomLevelChanged, this, - &VolumetricModifier::handleZoomLevelChange); QObject::connect(m_graph, &QAbstract3DGraph::currentFpsChanged, this, &VolumetricModifier::handleFpsChange); +// QObject::connect(m_graph->scene(), &Q3DScene::viewportChanged, this, +// &VolumetricModifier::handleFpsChange); } VolumetricModifier::~VolumetricModifier() @@ -121,26 +136,23 @@ void VolumetricModifier::adjustSliceZ(int value) } } -void VolumetricModifier::handleZoomLevelChange() -{ - // Zooming inside volumetric object causes ugly clipping issues, so restrict zoom level a bit - if (m_graph->scene()->activeCamera()->zoomLevel() > 220) - m_graph->scene()->activeCamera()->setZoomLevel(220); -} - -void VolumetricModifier::handleFpsChange(qreal fps) +void VolumetricModifier::handleFpsChange() { const QString fpsFormat = QStringLiteral("Fps: %1"); - int fps10 = int(fps * 10.0); + int fps10 = int(m_graph->currentFps() * 10.0); m_fpsLabel->setText(fpsFormat.arg(QString::number(qreal(fps10) / 10.0))); +// const QString sceneDimensionsFormat = QStringLiteral("%1 x %2"); +// m_fpsLabel->setText(sceneDimensionsFormat +// .arg(m_graph->scene()->viewport().width()) +// .arg(m_graph->scene()->viewport().height())); } void VolumetricModifier::createVolume() { m_volumeItem = new QCustom3DVolume; m_volumeItem->setTextureFormat(QImage::Format_ARGB32); - m_volumeItem->setRotation(QQuaternion::fromAxisAndAngle(1.0f, 1.0f, 0.0f, 10.0f)); - m_volumeItem->setPosition(QVector3D(-0.5f, 0.0f, 0.0f)); +// m_volumeItem->setRotation(QQuaternion::fromAxisAndAngle(1.0f, 1.0f, 0.0f, 10.0f)); + m_volumeItem->setPosition(QVector3D(-0.5f, 0.6f, 0.0f)); QImage logo; logo.load(QStringLiteral(":/logo.png")); @@ -161,10 +173,24 @@ void VolumetricModifier::createVolume() m_sliceIndexZ = m_volumeItem->textureWidth() / 2; QVector colorTable = m_volumeItem->colorTable(); + + // Hack some alpha to the picture + for (int i = 0; i < colorTable.size(); i++) { + if (qAlpha(colorTable.at(i)) > 0) { + colorTable[i] = qRgba(qRed(colorTable.at(i)), + qGreen(colorTable.at(i)), + qBlue(colorTable.at(i)), + 50); + } + } + + colorTable.append(qRgba(0, 0, 0, 0)); colorTable.append(qRgba(255, 0, 0, 255)); colorTable.append(qRgba(0, 255, 0, 255)); colorTable.append(qRgba(0, 0, 255, 255)); + m_volumeItem->setColorTable(colorTable); + int alphaIndex = colorTable.size() - 4; int redIndex = colorTable.size() - 3; int greenIndex = colorTable.size() - 2; int blueIndex = colorTable.size() - 1; @@ -173,18 +199,44 @@ void VolumetricModifier::createVolume() int depth = m_volumeItem->textureDepth(); int frameSize = width * height; qDebug() << width << height << depth << m_volumeItem->textureData()->size(); - m_volumeItem->setScaling(QVector3D(float(width) / float(depth) * 2.0f, - float(height) / float(depth) * 2.0f, - 2.0f)); +// m_volumeItem->setScaling(QVector3D(float(width) / float(depth) * 2.0f, +// float(height) / float(depth) * 2.0f, +// 2.0f)); + m_volumeItem->setScaling(QVector3D(0.4f, 0.4f, 0.4f)); uchar *data = m_volumeItem->textureData()->data(); uchar *p = data; // Change one picture using subtexture replacement - QImage flipped = logo.mirrored(); - m_volumeItem->setSubTextureData(101, flipped); +// QImage flipped = logo.mirrored(); +// m_volumeItem->setSubTextureData(101, flipped); + // Clean up the two extra pixels + p = data + width - 1; + for (int k = 0; k < depth; k++) { + for (int j = 0; j < height; j++) { + *p = alphaIndex; + p += width; + } + } + p = data + width - 2; + for (int k = 0; k < depth; k++) { + for (int j = 0; j < height; j++) { + *p = alphaIndex; + p += width; + } + } // Red first subtexture + p = data; + for (int j = 0; j < height; j++) { + for (int i = 0; i < width; i++) { + *p = redIndex; + p++; + } + } + + // Red last subtexture +// p = data + frameSize * (imageCount - 1); // for (int j = 0; j < height; j++) { // for (int i = 0; i < width; i++) { // *p = redIndex; @@ -192,45 +244,43 @@ void VolumetricModifier::createVolume() // } // } - // Red last subtexture - p = data + frameSize * (imageCount - 1); - for (int j = 0; j < height; j++) { - for (int i = 0; i < width; i++) { - *p = redIndex; - p++; - } - } +// // Blue second to last subtexture +// p = data + frameSize * (imageCount - 2); +// for (int j = 0; j < height; j++) { +// for (int i = 0; i < width; i++) { +// *p = blueIndex; +// p++; +// } +// } // Blue x = 0 - p = data; +// p = data; +// for (int k = 0; k < depth; k++) { +// for (int j = 0; j < height; j++) { +// *p = blueIndex; +// p += width; +// } +// } + + // Blue x = max + p = data + width - 1; for (int k = 0; k < depth; k++) { for (int j = 0; j < height; j++) { *p = blueIndex; p += width; } } - - // Blue x = max - p = data + width - 1; + // green x = max - 1 + p = data + width - 2; for (int k = 0; k < depth; k++) { for (int j = 0; j < height; j++) { - *p = blueIndex; + *p = greenIndex; p += width; } } // Green y = 0 -// p = data; -// for (int k = 0; k < depth; k++) { -// for (int i = 0; i < width; i++) { -// *p = greenIndex; -// p++; -// } -// p += (frameSize - width); -// } - - // Green y = max - p = data + frameSize - width; + p = data; for (int k = 0; k < depth; k++) { for (int i = 0; i < width; i++) { *p = greenIndex; @@ -238,13 +288,23 @@ void VolumetricModifier::createVolume() } p += (frameSize - width); } + + // Green y = max +// p = data + frameSize - width; +// for (int k = 0; k < depth; k++) { +// for (int i = 0; i < width; i++) { +// *p = greenIndex; +// p++; +// } +// p += (frameSize - width); +// } } void VolumetricModifier::createAnotherVolume() { m_volumeItem2 = new QCustom3DVolume; m_volumeItem2->setTextureFormat(QImage::Format_ARGB32); - m_volumeItem2->setPosition(QVector3D(0.5f, 0.0f, 0.0f)); + m_volumeItem2->setPosition(QVector3D(0.5f, -0.5f, 0.0f)); QImage logo; logo.load(QStringLiteral(":/logo.png")); @@ -280,3 +340,148 @@ void VolumetricModifier::createAnotherVolume() m_volumeItem2->setSubTextureData(101, flipped); } +void VolumetricModifier::createYetAnotherVolume() +{ + m_volumeItem3 = new QCustom3DVolume; + m_volumeItem3->setTextureFormat(QImage::Format_Indexed8); +// m_volumeItem2->setRotation(QQuaternion::fromAxisAndAngle(1.0f, 1.0f, 0.0f, 10.0f)); + m_volumeItem3->setPosition(QVector3D(-0.5f, -0.6f, 0.0f)); + +// m_volumeItem3->setTextureDimensions(m_volumeItem->textureDataWidth(), +// m_volumeItem->textureHeight(), +// m_volumeItem->textureDepth()); + m_volumeItem3->setTextureDimensions(200, 200, 200); + + QVector *tdata = new QVector(m_volumeItem3->textureDataWidth() + * m_volumeItem3->textureHeight() + * m_volumeItem3->textureDepth()); + tdata->fill(0); + m_volumeItem3->setTextureData(tdata); + + m_sliceIndexX = m_volumeItem3->textureWidth() / 2; + m_sliceIndexY = m_volumeItem3->textureWidth() / 2; + m_sliceIndexZ = m_volumeItem3->textureWidth() / 2; + + QVector colorTable = m_volumeItem->colorTable(); + colorTable[0] = qRgba(0, 0, 0, 0); + m_volumeItem3->setColorTable(colorTable); + int redIndex = colorTable.size() - 3; + int greenIndex = colorTable.size() - 2; + int blueIndex = colorTable.size() - 1; + int width = m_volumeItem3->textureDataWidth(); + int height = m_volumeItem3->textureHeight(); + int depth = m_volumeItem3->textureDepth(); + int frameSize = width * height; + qDebug() << width << height << depth << m_volumeItem3->textureData()->size(); + m_volumeItem3->setScaling(m_volumeItem->scaling()); + + uchar *data = tdata->data(); + uchar *p = data; + + // Red first subtexture +// for (int j = 0; j < height; j++) { +// for (int i = 0; i < width; i++) { +// *p = redIndex; +// p++; +// } +// } + + // Red last subtexture + p = data + frameSize * (depth - 1); + for (int j = 0; j < height; j++) { + for (int i = 0; i < width; i++) { + *p = redIndex; + p++; + } + } + + // green edge + p = data + frameSize * (depth - 1); + for (int i = 0; i < width; i++) { + *p = greenIndex; + p++; + } + for (int j = 2; j < height; j++) { + *p = greenIndex; + p += width - 1; + *p = j % 7 ? greenIndex : blueIndex; + p++; + } + for (int i = 0; i < width; i++) { + *p = greenIndex; + p++; + } + +// // Blue second to last subtexture +// p = data + frameSize * (depth - 2); +// for (int j = 0; j < height; j++) { +// for (int i = 0; i < width; i++) { +// *p = blueIndex; +// p++; +// } +// } + +// // green third to last subtexture +// p = data + frameSize * (depth - 3); +// for (int j = 0; j < height; j++) { +// for (int i = 0; i < width; i++) { +// *p = greenIndex; +// p++; +// } +// } + + // Blue x = 0 + p = data; + for (int k = 0; k < depth; k++) { + for (int j = 0; j < height; j++) { + *p = blueIndex; + p += width; + } + } + +// // Blue x = max +// p = data + width - 1; +// for (int k = 0; k < depth; k++) { +// for (int j = 0; j < height; j++) { +// *p = blueIndex; +// p += width; +// } +// } + + // Green y = 0 +// p = data; +// for (int k = 0; k < depth; k++) { +// for (int i = 0; i < width; i++) { +// *p = greenIndex; +// p++; +// } +// p += (frameSize - width); +// } + +// // Green y = max + p = data + frameSize - width; + for (int k = 0; k < depth; k++) { + for (int i = 0; i < width; i++) { + *p = greenIndex; + p++; + } + p += (frameSize - width); + } + + +// // Fill with alternating pixels +// p = data; +// for (int k = 0; k < (depth * width * height / 4); k++) { +// *p = greenIndex; +// p++; +// *p = greenIndex; +// p++; +// *p = blueIndex; +// p++; +// *p = redIndex; +// p++; +// } + + +} + diff --git a/tests/volumetrictest/volumetrictest.h b/tests/volumetrictest/volumetrictest.h index 677a1231..40d88192 100644 --- a/tests/volumetrictest/volumetrictest.h +++ b/tests/volumetrictest/volumetrictest.h @@ -21,6 +21,7 @@ #include #include +#include class QLabel; @@ -42,16 +43,18 @@ public slots: void adjustSliceX(int value); void adjustSliceY(int value); void adjustSliceZ(int value); - void handleZoomLevelChange(); - void handleFpsChange(qreal fps); + void handleFpsChange(); private: void createVolume(); void createAnotherVolume(); + void createYetAnotherVolume(); Q3DScatter *m_graph; QCustom3DVolume *m_volumeItem; QCustom3DVolume *m_volumeItem2; + QCustom3DVolume *m_volumeItem3; + QCustom3DItem *m_plainItem; int m_sliceIndexX; int m_sliceIndexY; int m_sliceIndexZ; diff --git a/tests/volumetrictest/volumetrictest.qrc b/tests/volumetrictest/volumetrictest.qrc index 90517ff1..5b9623f0 100644 --- a/tests/volumetrictest/volumetrictest.qrc +++ b/tests/volumetrictest/volumetrictest.qrc @@ -1,5 +1,6 @@ logo.png + cubeFilledFlat.obj -- cgit v1.2.3 From f9bb71fd11cce59d74e78202a1117c8abb3a2e44 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Fri, 22 Aug 2014 16:40:52 +0300 Subject: Implement API function for rendering volume slice to an image. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: Iea18967c3b525a8d4507a06e6541c85ed3abb470 Reviewed-by: Tomi Korpipää --- examples/datavisualization/volumetric/main.cpp | 22 +++++- .../datavisualization/volumetric/volumetric.cpp | 56 ++++++++++++--- examples/datavisualization/volumetric/volumetric.h | 4 ++ src/datavisualization/data/qcustom3dvolume.cpp | 83 +++++++++++++++++++++- src/datavisualization/data/qcustom3dvolume.h | 2 + .../engine/shaders/texture3dslice.frag | 4 +- tests/volumetrictest/main.cpp | 23 +++++- tests/volumetrictest/volumetrictest.cpp | 25 ++++++- tests/volumetrictest/volumetrictest.h | 4 ++ 9 files changed, 204 insertions(+), 19 deletions(-) diff --git a/examples/datavisualization/volumetric/main.cpp b/examples/datavisualization/volumetric/main.cpp index 66f62779..5a90070e 100644 --- a/examples/datavisualization/volumetric/main.cpp +++ b/examples/datavisualization/volumetric/main.cpp @@ -106,22 +106,42 @@ int main(int argc, char **argv) colorTableCheckBox->setText(QStringLiteral("Alternate color table")); colorTableCheckBox->setChecked(false); + QLabel *sliceImageXLabel = new QLabel(widget); + QLabel *sliceImageYLabel = new QLabel(widget); + QLabel *sliceImageZLabel = new QLabel(widget); + sliceImageXLabel->setMinimumSize(QSize(200, 100)); + sliceImageYLabel->setMinimumSize(QSize(200, 200)); + sliceImageZLabel->setMinimumSize(QSize(200, 100)); + sliceImageXLabel->setMaximumSize(QSize(200, 100)); + sliceImageYLabel->setMaximumSize(QSize(200, 200)); + sliceImageZLabel->setMaximumSize(QSize(200, 100)); + sliceImageXLabel->setFrameShape(QFrame::Box); + sliceImageYLabel->setFrameShape(QFrame::Box); + sliceImageZLabel->setFrameShape(QFrame::Box); + sliceImageXLabel->setScaledContents(true); + sliceImageYLabel->setScaledContents(true); + sliceImageZLabel->setScaledContents(true); + vLayout->addWidget(fpsCheckBox); vLayout->addWidget(fpsLabel); vLayout->addWidget(textureDetailGroupBox); vLayout->addWidget(colorTableCheckBox); vLayout->addWidget(sliceXCheckBox); vLayout->addWidget(sliceXSlider); + vLayout->addWidget(sliceImageXLabel); vLayout->addWidget(sliceYCheckBox); vLayout->addWidget(sliceYSlider); + vLayout->addWidget(sliceImageYLabel); vLayout->addWidget(sliceZCheckBox); - vLayout->addWidget(sliceZSlider, 1, Qt::AlignTop); + vLayout->addWidget(sliceZSlider); + vLayout->addWidget(sliceImageZLabel, 1, Qt::AlignTop); VolumetricModifier *modifier = new VolumetricModifier(graph); modifier->setFpsLabel(fpsLabel); modifier->setMediumDetailRB(mediumDetailRB); modifier->setHighDetailRB(highDetailRB); modifier->setSliceSliders(sliceXSlider, sliceYSlider, sliceZSlider); + modifier->setSliceLabels(sliceImageXLabel, sliceImageYLabel, sliceImageZLabel); QObject::connect(fpsCheckBox, &QCheckBox::stateChanged, modifier, &VolumetricModifier::setFpsMeasurement); diff --git a/examples/datavisualization/volumetric/volumetric.cpp b/examples/datavisualization/volumetric/volumetric.cpp index b4ea769c..a553ccf8 100644 --- a/examples/datavisualization/volumetric/volumetric.cpp +++ b/examples/datavisualization/volumetric/volumetric.cpp @@ -51,7 +51,10 @@ VolumetricModifier::VolumetricModifier(Q3DScatter *scatter) m_highDetailIndex(0), m_sliceSliderX(0), m_sliceSliderY(0), - m_sliceSliderZ(0) + m_sliceSliderZ(0), + m_sliceLabelX(0), + m_sliceLabelY(0), + m_sliceLabelZ(0) { m_graph->activeTheme()->setType(Q3DTheme::ThemeQt); m_graph->setShadowQuality(QAbstract3DGraph::ShadowQualityNone); @@ -95,12 +98,12 @@ VolumetricModifier::VolumetricModifier(Q3DScatter *scatter) } // The alternate color table. - // The first visible layer is a thin yellow one, and rest of the volume uses a smooth gradient. + // The first visible layer is a thin single color, and rest of the volume uses a smooth gradient. for (int i = 1; i < colorTableSize; i++) { if (i < cutOffColorIndex) m_colorTable2[i] = qRgba(0, 0, 0, 0); else if (i < cutOffColorIndex + 4) - m_colorTable2[i] = qRgba(255, 255, 0, 255); + m_colorTable2[i] = qRgba(75, 150, 0, 255); else m_colorTable2[i] = qRgba(i, 0, 255 - i, 255); } @@ -108,6 +111,7 @@ VolumetricModifier::VolumetricModifier(Q3DScatter *scatter) m_volumeItem->setColorTable(m_colorTable1); m_graph->addCustomItem(m_volumeItem); + m_timer.start(0); #else // OpenGL ES2 doesn't support 3D textures, so show a warning label instead @@ -152,6 +156,17 @@ void VolumetricModifier::setHighDetailRB(QRadioButton *button) m_highDetailRB = button; } +void VolumetricModifier::setSliceLabels(QLabel *xLabel, QLabel *yLabel, QLabel *zLabel) +{ + m_sliceLabelX = xLabel; + m_sliceLabelY = yLabel; + m_sliceLabelZ = zLabel; + + adjustSliceX(m_sliceSliderX->value()); + adjustSliceY(m_sliceSliderY->value()); + adjustSliceZ(m_sliceSliderZ->value()); +} + void VolumetricModifier::sliceX(int enabled) { if (m_volumeItem) @@ -173,22 +188,40 @@ void VolumetricModifier::sliceZ(int enabled) void VolumetricModifier::adjustSliceX(int value) { m_sliceIndexX = value / (1024 / m_volumeItem->textureWidth()); - if (m_volumeItem && m_volumeItem->sliceIndexX() != -1) - m_volumeItem->setSliceIndexX(m_sliceIndexX); + if (m_sliceIndexX == m_volumeItem->textureWidth()) + m_sliceIndexX--; + if (m_volumeItem) { + if (m_volumeItem->sliceIndexX() != -1) + m_volumeItem->setSliceIndexX(m_sliceIndexX); + m_sliceLabelX->setPixmap(QPixmap::fromImage( + m_volumeItem->renderSlice(Qt::XAxis, m_sliceIndexX))); + } } void VolumetricModifier::adjustSliceY(int value) { m_sliceIndexY = value / (1024 / m_volumeItem->textureHeight()); - if (m_volumeItem && m_volumeItem->sliceIndexY() != -1) - m_volumeItem->setSliceIndexY(m_sliceIndexY); + if (m_sliceIndexY == m_volumeItem->textureHeight()) + m_sliceIndexY--; + if (m_volumeItem) { + if (m_volumeItem->sliceIndexY() != -1) + m_volumeItem->setSliceIndexY(m_sliceIndexY); + m_sliceLabelY->setPixmap(QPixmap::fromImage( + m_volumeItem->renderSlice(Qt::YAxis, m_sliceIndexY))); + } } void VolumetricModifier::adjustSliceZ(int value) { m_sliceIndexZ = value / (1024 / m_volumeItem->textureDepth()); - if (m_volumeItem && m_volumeItem->sliceIndexZ() != -1) - m_volumeItem->setSliceIndexZ(m_sliceIndexZ); + if (m_sliceIndexZ == m_volumeItem->textureDepth()) + m_sliceIndexZ--; + if (m_volumeItem) { + if (m_volumeItem->sliceIndexZ() != -1) + m_volumeItem->setSliceIndexZ(m_sliceIndexZ); + m_sliceLabelZ->setPixmap(QPixmap::fromImage( + m_volumeItem->renderSlice(Qt::ZAxis, m_sliceIndexZ))); + } } void VolumetricModifier::handleZoomLevelChange() @@ -282,6 +315,11 @@ void VolumetricModifier::changeColorTable(int enabled) m_volumeItem->setColorTable(m_colorTable2); else m_volumeItem->setColorTable(m_colorTable1); + + // Rerender image labels + adjustSliceX(m_sliceSliderX->value()); + adjustSliceY(m_sliceSliderY->value()); + adjustSliceZ(m_sliceSliderZ->value()); } int VolumetricModifier::createVolume(int textureSize, int startIndex, int count, diff --git a/examples/datavisualization/volumetric/volumetric.h b/examples/datavisualization/volumetric/volumetric.h index bb7fb3fe..eb8a4172 100644 --- a/examples/datavisualization/volumetric/volumetric.h +++ b/examples/datavisualization/volumetric/volumetric.h @@ -40,6 +40,7 @@ public: void setFpsLabel(QLabel *fpsLabel); void setMediumDetailRB(QRadioButton *button); void setHighDetailRB(QRadioButton *button); + void setSliceLabels(QLabel *xLabel, QLabel *yLabel, QLabel *zLabel); public slots: void sliceX(int enabled); @@ -82,6 +83,9 @@ private: QSlider *m_sliceSliderZ; QVector m_colorTable1; QVector m_colorTable2; + QLabel *m_sliceLabelX; + QLabel *m_sliceLabelY; + QLabel *m_sliceLabelZ; }; #endif diff --git a/src/datavisualization/data/qcustom3dvolume.cpp b/src/datavisualization/data/qcustom3dvolume.cpp index a7bbae9b..cab79ac0 100644 --- a/src/datavisualization/data/qcustom3dvolume.cpp +++ b/src/datavisualization/data/qcustom3dvolume.cpp @@ -577,6 +577,86 @@ QImage::Format QCustom3DVolume::textureFormat() const return dptrc()->m_textureFormat; } +/*! + * Renders the slice specified by \a index along \a axis into an image. + * The texture format of this object is used. + * + * \return the rendered image of the slice, or a null image if invalid index is specified. + * + * \sa textureFormat + */ +QImage QCustom3DVolume::renderSlice(Qt::Axis axis, int index) +{ + if (index < 0) + return QImage(); + + int x; + int y; + if (axis == Qt::XAxis) { + if (index >= textureWidth()) + return QImage(); + x = textureDepth(); + y = textureHeight(); + } else if (axis == Qt::YAxis) { + if (index >= textureHeight()) + return QImage(); + x = textureWidth(); + y = textureDepth(); + } else { + if (index >= textureDepth()) + return QImage(); + x = textureWidth(); + y = textureHeight(); + } + + int padding = 0; + int pixelWidth = 4; + if (textureFormat() == QImage::Format_Indexed8) { + padding = x % 4; + pixelWidth = 1; + } + QVector data((x + padding) * y * pixelWidth); + int frameSize = textureDataWidth() * textureHeight(); + + int dataIndex = 0; + if (axis == Qt::XAxis) { + for (int i = 0; i < y; i++) { + const uchar *p = textureData()->constData() + + (index * pixelWidth) + (textureDataWidth() * i); + for (int j = 0; j < x; j++) { + data[dataIndex++] = *p; + for (int k = 1; k < pixelWidth; k++) + data[dataIndex++] = *(p + k); + p += frameSize; + } + } + } else if (axis == Qt::YAxis) { + for (int i = 0; i < y; i++) { + const uchar *p = textureData()->constData() + (index * textureDataWidth()) + + (frameSize * i); + for (int j = 0; j < (x * pixelWidth); j++) { + data[dataIndex++] = *p; + p++; + } + } + } else { + for (int i = 0; i < y; i++) { + const uchar *p = textureData()->constData() + (index * frameSize) + + (textureDataWidth() * i); + for (int j = 0; j < (x * pixelWidth); j++) { + data[dataIndex++] = *p; + p++; + } + } + } + + QImage image(data.constData(), x, y, x * pixelWidth, textureFormat()); + image.bits(); // Call bits() to detach the new image from local data + if (textureFormat() == QImage::Format_Indexed8) + image.setColorTable(colorTable()); + + return image; +} /*! * \internal @@ -637,9 +717,6 @@ QCustom3DVolumePrivate::QCustom3DVolumePrivate(QCustom3DVolume *q, const QVector if (m_textureDepth < 0) m_textureDepth = 0; - if (m_colorTable.size() != 256) - m_colorTable.clear(); - if (m_textureFormat != QImage::Format_Indexed8) m_textureFormat = QImage::Format_ARGB32; diff --git a/src/datavisualization/data/qcustom3dvolume.h b/src/datavisualization/data/qcustom3dvolume.h index 498922e8..00733d17 100644 --- a/src/datavisualization/data/qcustom3dvolume.h +++ b/src/datavisualization/data/qcustom3dvolume.h @@ -80,6 +80,8 @@ public: void setTextureFormat(QImage::Format format); QImage::Format textureFormat() const; + QImage renderSlice(Qt::Axis axis, int index); + signals: void textureWidthChanged(int value); void textureHeightChanged(int value); diff --git a/src/datavisualization/engine/shaders/texture3dslice.frag b/src/datavisualization/engine/shaders/texture3dslice.frag index 409ab41d..8870b26d 100644 --- a/src/datavisualization/engine/shaders/texture3dslice.frag +++ b/src/datavisualization/engine/shaders/texture3dslice.frag @@ -33,8 +33,8 @@ void main() { if (rayDir.z < 0) boxBounds.z = -1.0; highp vec3 t = (boxBounds - rayStart) * invRayDir; - tFar = max(t.x, t.y); - tFar = max(tFar, t.z); + tFar = min(t.x, t.y); + tFar = min(tFar, t.z); } highp vec3 xPoint = vec3(volumeSliceIndices.x, 0, 0); diff --git a/tests/volumetrictest/main.cpp b/tests/volumetrictest/main.cpp index a15caf88..e838c43a 100644 --- a/tests/volumetrictest/main.cpp +++ b/tests/volumetrictest/main.cpp @@ -77,16 +77,37 @@ int main(int argc, char **argv) QLabel *fpsLabel = new QLabel(QStringLiteral("Fps: "), widget); + QLabel *sliceImageXLabel = new QLabel(widget); + QLabel *sliceImageYLabel = new QLabel(widget); + QLabel *sliceImageZLabel = new QLabel(widget); + sliceImageXLabel->setMinimumSize(QSize(200, 100)); + sliceImageYLabel->setMinimumSize(QSize(200, 200)); + sliceImageZLabel->setMinimumSize(QSize(200, 100)); + sliceImageXLabel->setMaximumSize(QSize(200, 100)); + sliceImageYLabel->setMaximumSize(QSize(200, 200)); + sliceImageZLabel->setMaximumSize(QSize(200, 100)); + sliceImageXLabel->setFrameShape(QFrame::Box); + sliceImageYLabel->setFrameShape(QFrame::Box); + sliceImageZLabel->setFrameShape(QFrame::Box); + sliceImageXLabel->setScaledContents(true); + sliceImageYLabel->setScaledContents(true); + sliceImageZLabel->setScaledContents(true); + vLayout->addWidget(fpsLabel); vLayout->addWidget(sliceXCheckBox); vLayout->addWidget(sliceXSlider); + vLayout->addWidget(sliceImageXLabel); vLayout->addWidget(sliceYCheckBox); vLayout->addWidget(sliceYSlider); + vLayout->addWidget(sliceImageYLabel); vLayout->addWidget(sliceZCheckBox); - vLayout->addWidget(sliceZSlider, 1, Qt::AlignTop); + vLayout->addWidget(sliceZSlider); + vLayout->addWidget(sliceImageZLabel, 1, Qt::AlignTop); + VolumetricModifier *modifier = new VolumetricModifier(graph); modifier->setFpsLabel(fpsLabel); + modifier->setSliceLabels(sliceImageXLabel, sliceImageYLabel, sliceImageZLabel); QObject::connect(sliceXCheckBox, &QCheckBox::stateChanged, modifier, &VolumetricModifier::sliceX); diff --git a/tests/volumetrictest/volumetrictest.cpp b/tests/volumetrictest/volumetrictest.cpp index 701cfc04..3485dd24 100644 --- a/tests/volumetrictest/volumetrictest.cpp +++ b/tests/volumetrictest/volumetrictest.cpp @@ -66,6 +66,8 @@ VolumetricModifier::VolumetricModifier(Q3DScatter *scatter) m_graph->addCustomItem(m_plainItem); //m_graph->setMeasureFps(true); + + QObject::connect(m_graph, &QAbstract3DGraph::currentFpsChanged, this, &VolumetricModifier::handleFpsChange); // QObject::connect(m_graph->scene(), &Q3DScene::viewportChanged, this, @@ -110,6 +112,9 @@ void VolumetricModifier::adjustSliceX(int value) m_volumeItem->setSliceIndexX(m_sliceIndexX); m_volumeItem2->setSliceIndexX(m_sliceIndexX); } + m_sliceLabelX->setPixmap(QPixmap::fromImage( + m_volumeItem2->renderSlice(Qt::XAxis, m_sliceIndexX))); + } void VolumetricModifier::adjustSliceY(int value) @@ -122,6 +127,8 @@ void VolumetricModifier::adjustSliceY(int value) m_volumeItem->setSliceIndexY(m_sliceIndexY); m_volumeItem2->setSliceIndexY(m_sliceIndexY); } + m_sliceLabelY->setPixmap(QPixmap::fromImage( + m_volumeItem2->renderSlice(Qt::YAxis, m_sliceIndexY))); } void VolumetricModifier::adjustSliceZ(int value) @@ -134,6 +141,8 @@ void VolumetricModifier::adjustSliceZ(int value) m_volumeItem->setSliceIndexZ(m_sliceIndexZ); m_volumeItem2->setSliceIndexZ(m_sliceIndexZ); } + m_sliceLabelZ->setPixmap(QPixmap::fromImage( + m_volumeItem2->renderSlice(Qt::ZAxis, m_sliceIndexZ))); } void VolumetricModifier::handleFpsChange() @@ -208,8 +217,8 @@ void VolumetricModifier::createVolume() uchar *p = data; // Change one picture using subtexture replacement -// QImage flipped = logo.mirrored(); -// m_volumeItem->setSubTextureData(101, flipped); + QImage flipped = logo.mirrored(); + m_volumeItem->setSubTextureData(100, flipped); // Clean up the two extra pixels p = data + width - 1; @@ -337,7 +346,7 @@ void VolumetricModifier::createAnotherVolume() // Change one picture using subtexture replacement QImage flipped = logo.mirrored(); - m_volumeItem2->setSubTextureData(101, flipped); + m_volumeItem2->setSubTextureData(100, flipped); } void VolumetricModifier::createYetAnotherVolume() @@ -485,3 +494,13 @@ void VolumetricModifier::createYetAnotherVolume() } +void VolumetricModifier::setSliceLabels(QLabel *xLabel, QLabel *yLabel, QLabel *zLabel) +{ + m_sliceLabelX = xLabel; + m_sliceLabelY = yLabel; + m_sliceLabelZ = zLabel; + + adjustSliceX(512); + adjustSliceY(512); + adjustSliceZ(512); +} diff --git a/tests/volumetrictest/volumetrictest.h b/tests/volumetrictest/volumetrictest.h index 40d88192..f21fd528 100644 --- a/tests/volumetrictest/volumetrictest.h +++ b/tests/volumetrictest/volumetrictest.h @@ -35,6 +35,7 @@ public: ~VolumetricModifier(); void setFpsLabel(QLabel *fpsLabel); + void setSliceLabels(QLabel *xLabel, QLabel *yLabel, QLabel *zLabel); public slots: void sliceX(int enabled); @@ -59,6 +60,9 @@ private: int m_sliceIndexY; int m_sliceIndexZ; QLabel *m_fpsLabel; + QLabel *m_sliceLabelX; + QLabel *m_sliceLabelY; + QLabel *m_sliceLabelZ; }; #endif -- cgit v1.2.3 From 5a51d06ec8f0210f51e65abfde9f868ab7bfa8ef Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Mon, 25 Aug 2014 13:16:04 +0300 Subject: Add alpha multiplier to QCustom3DVolume api MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I856c4166513f6d6f7b73fd52bc46d52ab1b8fdff Reviewed-by: Tomi Korpipää Reviewed-by: Mika Salmela --- examples/datavisualization/volumetric/main.cpp | 41 +++- .../datavisualization/volumetric/volumetric.cpp | 44 +++- examples/datavisualization/volumetric/volumetric.h | 4 + src/datavisualization/data/customrenderitem.cpp | 8 +- src/datavisualization/data/customrenderitem_p.h | 12 +- src/datavisualization/data/qcustom3dvolume.cpp | 251 +++++++++++++++------ src/datavisualization/data/qcustom3dvolume.h | 10 +- src/datavisualization/data/qcustom3dvolume_p.h | 10 +- .../engine/abstract3drenderer.cpp | 10 + .../engine/shaders/texture3d.frag | 14 +- .../engine/shaders/texture3dslice.frag | 54 +++-- src/datavisualization/utils/shaderhelper.cpp | 16 ++ src/datavisualization/utils/shaderhelper_p.h | 4 + tests/volumetrictest/volumetrictest.cpp | 4 +- 14 files changed, 372 insertions(+), 110 deletions(-) diff --git a/examples/datavisualization/volumetric/main.cpp b/examples/datavisualization/volumetric/main.cpp index 5a90070e..84062969 100644 --- a/examples/datavisualization/volumetric/main.cpp +++ b/examples/datavisualization/volumetric/main.cpp @@ -36,16 +36,18 @@ int main(int argc, char **argv) QWidget *container = QWidget::createWindowContainer(graph); QSize screenSize = graph->screen()->size(); - container->setMinimumSize(QSize(screenSize.width() / 2, screenSize.height() / 1.5)); + container->setMinimumSize(QSize(screenSize.width() / 3, screenSize.height() / 3)); container->setMaximumSize(screenSize); container->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); container->setFocusPolicy(Qt::StrongFocus); - QWidget *widget = new QWidget; + QWidget *widget = new QWidget(); QHBoxLayout *hLayout = new QHBoxLayout(widget); QVBoxLayout *vLayout = new QVBoxLayout(); + QVBoxLayout *vLayout2 = new QVBoxLayout(); hLayout->addWidget(container, 1); hLayout->addLayout(vLayout); + hLayout->addLayout(vLayout2); widget->setWindowTitle(QStringLiteral("Volumetric Object Example")); @@ -122,10 +124,17 @@ int main(int argc, char **argv) sliceImageYLabel->setScaledContents(true); sliceImageZLabel->setScaledContents(true); - vLayout->addWidget(fpsCheckBox); - vLayout->addWidget(fpsLabel); - vLayout->addWidget(textureDetailGroupBox); - vLayout->addWidget(colorTableCheckBox); + QSlider *alphaMultiplierSlider = new QSlider(Qt::Horizontal, widget); + alphaMultiplierSlider->setMinimum(0); + alphaMultiplierSlider->setMaximum(139); + alphaMultiplierSlider->setValue(100); + alphaMultiplierSlider->setEnabled(true); + QLabel *alphaMultiplierLabel = new QLabel(QStringLiteral("Alpha multiplier: 1.0")); + + QCheckBox *preserveOpacityCheckBox = new QCheckBox(widget); + preserveOpacityCheckBox->setText(QStringLiteral("Preserve opacity")); + preserveOpacityCheckBox->setChecked(true); + vLayout->addWidget(sliceXCheckBox); vLayout->addWidget(sliceXSlider); vLayout->addWidget(sliceImageXLabel); @@ -135,6 +144,13 @@ int main(int argc, char **argv) vLayout->addWidget(sliceZCheckBox); vLayout->addWidget(sliceZSlider); vLayout->addWidget(sliceImageZLabel, 1, Qt::AlignTop); + vLayout2->addWidget(fpsCheckBox); + vLayout2->addWidget(fpsLabel); + vLayout2->addWidget(textureDetailGroupBox); + vLayout2->addWidget(colorTableCheckBox); + vLayout2->addWidget(alphaMultiplierLabel); + vLayout2->addWidget(alphaMultiplierSlider); + vLayout2->addWidget(preserveOpacityCheckBox, 1, Qt::AlignTop); VolumetricModifier *modifier = new VolumetricModifier(graph); modifier->setFpsLabel(fpsLabel); @@ -142,6 +158,7 @@ int main(int argc, char **argv) modifier->setHighDetailRB(highDetailRB); modifier->setSliceSliders(sliceXSlider, sliceYSlider, sliceZSlider); modifier->setSliceLabels(sliceImageXLabel, sliceImageYLabel, sliceImageZLabel); + modifier->setAlphaMultiplierLabel(alphaMultiplierLabel); QObject::connect(fpsCheckBox, &QCheckBox::stateChanged, modifier, &VolumetricModifier::setFpsMeasurement); @@ -157,14 +174,18 @@ int main(int argc, char **argv) &VolumetricModifier::adjustSliceY); QObject::connect(sliceZSlider, &QSlider::valueChanged, modifier, &VolumetricModifier::adjustSliceZ); - QObject::connect(lowDetailRB, &QRadioButton::toggled, modifier, + QObject::connect(lowDetailRB, &QRadioButton::toggled, modifier, &VolumetricModifier::toggleLowDetail); - QObject::connect(mediumDetailRB, &QRadioButton::toggled, modifier, + QObject::connect(mediumDetailRB, &QRadioButton::toggled, modifier, &VolumetricModifier::toggleMediumDetail); - QObject::connect(highDetailRB, &QRadioButton::toggled, modifier, + QObject::connect(highDetailRB, &QRadioButton::toggled, modifier, &VolumetricModifier::toggleHighDetail); - QObject::connect(colorTableCheckBox, &QCheckBox::stateChanged, modifier, + QObject::connect(colorTableCheckBox, &QCheckBox::stateChanged, modifier, &VolumetricModifier::changeColorTable); + QObject::connect(preserveOpacityCheckBox, &QCheckBox::stateChanged, modifier, + &VolumetricModifier::setPreserveOpacity); + QObject::connect(alphaMultiplierSlider, &QSlider::valueChanged, modifier, + &VolumetricModifier::adjustAlphaMultiplier); widget->show(); return app.exec(); diff --git a/examples/datavisualization/volumetric/volumetric.cpp b/examples/datavisualization/volumetric/volumetric.cpp index a553ccf8..56f02dcb 100644 --- a/examples/datavisualization/volumetric/volumetric.cpp +++ b/examples/datavisualization/volumetric/volumetric.cpp @@ -80,7 +80,7 @@ VolumetricModifier::VolumetricModifier(Q3DScatter *scatter) // Both tables have a fully transparent colors to fill outer portions of the volume. // The primary color table. - // The first visible layer, red, is somewhat transparent. Rest of to colors are opaque. + // The top two layers are transparent. m_colorTable1.resize(colorTableSize); m_colorTable2.resize(colorTableSize); @@ -88,9 +88,9 @@ VolumetricModifier::VolumetricModifier(Q3DScatter *scatter) if (i < cutOffColorIndex) m_colorTable1[i] = qRgba(0, 0, 0, 0); else if (i < 60) - m_colorTable1[i] = qRgba((i * 2) + 120, 0, 0, 20); + m_colorTable1[i] = qRgba((i * 2) + 120, 0, 0, 15); else if (i < 120) - m_colorTable1[i] = qRgba(0, ((i - 60) * 2) + 120, 0, 255); + m_colorTable1[i] = qRgba(0, ((i - 60) * 2) + 120, 0, 50); else if (i < 180) m_colorTable1[i] = qRgba(0, 0, ((i - 120) * 2) + 120, 255); else @@ -98,14 +98,15 @@ VolumetricModifier::VolumetricModifier(Q3DScatter *scatter) } // The alternate color table. - // The first visible layer is a thin single color, and rest of the volume uses a smooth gradient. + // The first visible layer is a thin opaque color, and rest of the volume uses a smooth + // transparent gradient. for (int i = 1; i < colorTableSize; i++) { if (i < cutOffColorIndex) m_colorTable2[i] = qRgba(0, 0, 0, 0); else if (i < cutOffColorIndex + 4) m_colorTable2[i] = qRgba(75, 150, 0, 255); else - m_colorTable2[i] = qRgba(i, 0, 255 - i, 255); + m_colorTable2[i] = qRgba(i, 0, 255 - i, i); } m_volumeItem->setColorTable(m_colorTable1); @@ -167,6 +168,11 @@ void VolumetricModifier::setSliceLabels(QLabel *xLabel, QLabel *yLabel, QLabel * adjustSliceZ(m_sliceSliderZ->value()); } +void VolumetricModifier::setAlphaMultiplierLabel(QLabel *label) +{ + m_alphaMultiplierLabel = label; +} + void VolumetricModifier::sliceX(int enabled) { if (m_volumeItem) @@ -322,6 +328,34 @@ void VolumetricModifier::changeColorTable(int enabled) adjustSliceZ(m_sliceSliderZ->value()); } +void VolumetricModifier::setPreserveOpacity(bool enabled) +{ + m_volumeItem->setPreserveOpacity(enabled); + + // Rerender image labels + adjustSliceX(m_sliceSliderX->value()); + adjustSliceY(m_sliceSliderY->value()); + adjustSliceZ(m_sliceSliderZ->value()); +} + +void VolumetricModifier::adjustAlphaMultiplier(int value) +{ + float mult; + if (value > 100) + mult = float(value - 99) / 2.0f; + else + mult = float(value) / float(500 - value * 4); + m_volumeItem->setAlphaMultiplier(mult); + QString labelFormat = QStringLiteral("Alpha multiplier: %1"); + m_alphaMultiplierLabel->setText(labelFormat.arg( + QString::number(m_volumeItem->alphaMultiplier(), 'f', 3))); + + // Rerender image labels + adjustSliceX(m_sliceSliderX->value()); + adjustSliceY(m_sliceSliderY->value()); + adjustSliceZ(m_sliceSliderZ->value()); +} + int VolumetricModifier::createVolume(int textureSize, int startIndex, int count, QVector *textureData) { diff --git a/examples/datavisualization/volumetric/volumetric.h b/examples/datavisualization/volumetric/volumetric.h index eb8a4172..497506ad 100644 --- a/examples/datavisualization/volumetric/volumetric.h +++ b/examples/datavisualization/volumetric/volumetric.h @@ -41,6 +41,7 @@ public: void setMediumDetailRB(QRadioButton *button); void setHighDetailRB(QRadioButton *button); void setSliceLabels(QLabel *xLabel, QLabel *yLabel, QLabel *zLabel); + void setAlphaMultiplierLabel(QLabel *label); public slots: void sliceX(int enabled); @@ -58,6 +59,8 @@ public slots: void setFpsMeasurement(bool enabled); void setSliceSliders(QSlider *sliderX, QSlider *sliderY, QSlider *sliderZ); void changeColorTable(int enabled); + void setPreserveOpacity(bool enabled); + void adjustAlphaMultiplier(int value); private: @@ -86,6 +89,7 @@ private: QLabel *m_sliceLabelX; QLabel *m_sliceLabelY; QLabel *m_sliceLabelZ; + QLabel *m_alphaMultiplierLabel; }; #endif diff --git a/src/datavisualization/data/customrenderitem.cpp b/src/datavisualization/data/customrenderitem.cpp index 3eb68845..c316fd38 100644 --- a/src/datavisualization/data/customrenderitem.cpp +++ b/src/datavisualization/data/customrenderitem.cpp @@ -36,7 +36,13 @@ CustomRenderItem::CustomRenderItem() m_textureWidth(0), m_textureHeight(0), m_textureDepth(0), - m_isVolume(false) + m_isVolume(false), + m_textureFormat(QImage::Format_ARGB32), + m_sliceIndexX(-1), + m_sliceIndexY(-1), + m_sliceIndexZ(-1), + m_alphaMultiplier(1.0f), + m_preserveOpacity(true) { } diff --git a/src/datavisualization/data/customrenderitem_p.h b/src/datavisualization/data/customrenderitem_p.h index e21b6f39..5428ce43 100644 --- a/src/datavisualization/data/customrenderitem_p.h +++ b/src/datavisualization/data/customrenderitem_p.h @@ -89,9 +89,13 @@ public: inline void setSliceIndexX(int index) { m_sliceIndexX = index; } inline void setSliceIndexY(int index) { m_sliceIndexY = index; } inline void setSliceIndexZ(int index) { m_sliceIndexZ = index; } - int sliceIndexX() const { return m_sliceIndexX; } - int sliceIndexY() const { return m_sliceIndexY; } - int sliceIndexZ() const { return m_sliceIndexZ; } + inline int sliceIndexX() const { return m_sliceIndexX; } + inline int sliceIndexY() const { return m_sliceIndexY; } + inline int sliceIndexZ() const { return m_sliceIndexZ; } + inline void setAlphaMultiplier(float mult) { m_alphaMultiplier = mult; } + inline float alphaMultiplier() const { return m_alphaMultiplier; } + inline void setPreserveOpacity(bool enable) { m_preserveOpacity = enable; } + inline bool preserveOpacity() const { return m_preserveOpacity; } private: Q_DISABLE_COPY(CustomRenderItem) @@ -120,6 +124,8 @@ private: int m_sliceIndexX; int m_sliceIndexY; int m_sliceIndexZ; + float m_alphaMultiplier; + bool m_preserveOpacity; }; typedef QHash CustomRenderItemArray; diff --git a/src/datavisualization/data/qcustom3dvolume.cpp b/src/datavisualization/data/qcustom3dvolume.cpp index cab79ac0..c1a77dba 100644 --- a/src/datavisualization/data/qcustom3dvolume.cpp +++ b/src/datavisualization/data/qcustom3dvolume.cpp @@ -111,6 +111,30 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION * \sa QCustom3DVolume::textureData */ +/*! + * \qmlproperty real Custom3DVolume::alphaMultiplier + * + * The alpha value of every texel of the volume texture is multiplied with this value at + * the render time. This can be used to introduce uniform transparency to the volume. + * If preserveOpacity is \c{true}, only texels with at least some transparency to begin with are + * affected, and fully opaque texels are not affected. + * The value must not be negative. + * Defaults to \c{1.0}. + * + * \sa preserveOpacity + */ + +/*! + * \qmlproperty bool Custom3DVolume::preserveOpacity + * + * If this property value is \c{true}, alphaMultiplier is only applied to texels that already have + * some transparency. If it is \c{false}, the multiplier is applied to the alpha value of all + * texels. + * Defaults to \c{true}. + * + * \sa alphaMultiplier + */ + /*! * Constructs QCustom3DVolume with given \a parent. */ @@ -578,84 +602,72 @@ QImage::Format QCustom3DVolume::textureFormat() const } /*! - * Renders the slice specified by \a index along \a axis into an image. - * The texture format of this object is used. + * \property QCustom3DVolume::alphaMultiplier * - * \return the rendered image of the slice, or a null image if invalid index is specified. + * The alpha value of every texel of the volume texture is multiplied with this value at + * the render time. This can be used to introduce uniform transparency to the volume. + * If preserveOpacity is \c{true}, only texels with at least some transparency to begin with are + * affected, and fully opaque texels are not affected. + * The value must not be negative. + * Defaults to \c{1.0f}. * - * \sa textureFormat + * \sa preserveOpacity, textureData */ -QImage QCustom3DVolume::renderSlice(Qt::Axis axis, int index) +void QCustom3DVolume::setAlphaMultiplier(float mult) { - if (index < 0) - return QImage(); - - int x; - int y; - if (axis == Qt::XAxis) { - if (index >= textureWidth()) - return QImage(); - x = textureDepth(); - y = textureHeight(); - } else if (axis == Qt::YAxis) { - if (index >= textureHeight()) - return QImage(); - x = textureWidth(); - y = textureDepth(); + if (mult >= 0.0f) { + if (dptr()->m_alphaMultiplier != mult) { + dptr()->m_alphaMultiplier = mult; + dptr()->m_dirtyBitsVolume.alphaDirty = true; + emit alphaMultiplierChanged(mult); + emit dptr()->needUpdate(); + } } else { - if (index >= textureDepth()) - return QImage(); - x = textureWidth(); - y = textureHeight(); + qWarning() << __FUNCTION__ << "Attempted to set negative multiplier."; } +} - int padding = 0; - int pixelWidth = 4; - if (textureFormat() == QImage::Format_Indexed8) { - padding = x % 4; - pixelWidth = 1; - } - QVector data((x + padding) * y * pixelWidth); - int frameSize = textureDataWidth() * textureHeight(); +float QCustom3DVolume::alphaMultiplier() const +{ + return dptrc()->m_alphaMultiplier; +} - int dataIndex = 0; - if (axis == Qt::XAxis) { - for (int i = 0; i < y; i++) { - const uchar *p = textureData()->constData() - + (index * pixelWidth) + (textureDataWidth() * i); - for (int j = 0; j < x; j++) { - data[dataIndex++] = *p; - for (int k = 1; k < pixelWidth; k++) - data[dataIndex++] = *(p + k); - p += frameSize; - } - } - } else if (axis == Qt::YAxis) { - for (int i = 0; i < y; i++) { - const uchar *p = textureData()->constData() + (index * textureDataWidth()) - + (frameSize * i); - for (int j = 0; j < (x * pixelWidth); j++) { - data[dataIndex++] = *p; - p++; - } - } - } else { - for (int i = 0; i < y; i++) { - const uchar *p = textureData()->constData() + (index * frameSize) - + (textureDataWidth() * i); - for (int j = 0; j < (x * pixelWidth); j++) { - data[dataIndex++] = *p; - p++; - } - } +/*! + * \property QCustom3DVolume::preserveOpacity + * + * If this property value is \c{true}, alphaMultiplier is only applied to texels that already have + * some transparency. If it is \c{false}, the multiplier is applied to the alpha value of all + * texels. + * Defaults to \c{true}. + * + * \sa alphaMultiplier + */ +void QCustom3DVolume::setPreserveOpacity(bool enable) +{ + if (dptr()->m_preserveOpacity != enable) { + dptr()->m_preserveOpacity = enable; + dptr()->m_dirtyBitsVolume.alphaDirty = true; + emit preserveOpacityChanged(enable); + emit dptr()->needUpdate(); } +} - QImage image(data.constData(), x, y, x * pixelWidth, textureFormat()); - image.bits(); // Call bits() to detach the new image from local data - if (textureFormat() == QImage::Format_Indexed8) - image.setColorTable(colorTable()); +bool QCustom3DVolume::preserveOpacity() const +{ + return dptrc()->m_preserveOpacity; +} - return image; +/*! + * Renders the slice specified by \a index along \a axis into an image. + * The texture format of this object is used. + * + * \return the rendered image of the slice, or a null image if invalid index is specified. + * + * \sa textureFormat + */ +QImage QCustom3DVolume::renderSlice(Qt::Axis axis, int index) +{ + return dptr()->renderSlice(axis, index); } /*! @@ -683,7 +695,9 @@ QCustom3DVolumePrivate::QCustom3DVolumePrivate(QCustom3DVolume *q) : m_textureHeight(0), m_textureDepth(0), m_textureFormat(QImage::Format_ARGB32), - m_textureData(0) + m_textureData(0), + m_alphaMultiplier(1.0f), + m_preserveOpacity(true) { m_isVolumeItem = true; m_meshFile = QStringLiteral(":/defaultMeshes/barFull"); @@ -705,7 +719,9 @@ QCustom3DVolumePrivate::QCustom3DVolumePrivate(QCustom3DVolume *q, const QVector m_textureDepth(textureDepth), m_textureFormat(textureFormat), m_colorTable(colorTable), - m_textureData(textureData) + m_textureData(textureData), + m_alphaMultiplier(1.0f), + m_preserveOpacity(true) { m_isVolumeItem = true; m_shadowCasting = false; @@ -738,6 +754,103 @@ void QCustom3DVolumePrivate::resetDirtyBits() m_dirtyBitsVolume.textureFormatDirty = false; } +QImage QCustom3DVolumePrivate::renderSlice(Qt::Axis axis, int index) +{ + if (index < 0) + return QImage(); + + int x; + int y; + if (axis == Qt::XAxis) { + if (index >= m_textureWidth) + return QImage(); + x = m_textureDepth; + y = m_textureHeight; + } else if (axis == Qt::YAxis) { + if (index >= m_textureHeight) + return QImage(); + x = m_textureWidth; + y = m_textureDepth; + } else { + if (index >= m_textureDepth) + return QImage(); + x = m_textureWidth; + y = m_textureHeight; + } + + int padding = 0; + int pixelWidth = 4; + int dataWidth = qptr()->textureDataWidth(); + if (m_textureFormat == QImage::Format_Indexed8) { + padding = x % 4; + pixelWidth = 1; + } + QVector data((x + padding) * y * pixelWidth); + int frameSize = qptr()->textureDataWidth() * m_textureHeight; + + int dataIndex = 0; + if (axis == Qt::XAxis) { + for (int i = 0; i < y; i++) { + const uchar *p = m_textureData->constData() + + (index * pixelWidth) + (dataWidth * i); + for (int j = 0; j < x; j++) { + for (int k = 0; k < pixelWidth; k++) + data[dataIndex++] = *(p + k); + p += frameSize; + } + } + } else if (axis == Qt::YAxis) { + for (int i = 0; i < y; i++) { + const uchar *p = m_textureData->constData() + (index * dataWidth) + + (frameSize * i); + for (int j = 0; j < (x * pixelWidth); j++) { + data[dataIndex++] = *p; + p++; + } + } + } else { + for (int i = 0; i < y; i++) { + const uchar *p = m_textureData->constData() + (index * frameSize) + (dataWidth * i); + for (int j = 0; j < (x * pixelWidth); j++) { + data[dataIndex++] = *p; + p++; + } + } + } + + if (m_textureFormat != QImage::Format_Indexed8 && m_alphaMultiplier != 1.0f) { + for (int i = pixelWidth - 1; i < data.size(); i += pixelWidth) + data[i] = static_cast(multipliedAlphaValue(data.at(i))); + } + + QImage image(data.constData(), x, y, x * pixelWidth, m_textureFormat); + image.bits(); // Call bits() to detach the new image from local data + if (m_textureFormat == QImage::Format_Indexed8) { + QVector colorTable = m_colorTable; + if (m_alphaMultiplier != 1.0f) { + for (int i = 0; i < colorTable.size(); i++) { + QRgb curCol = colorTable.at(i); + int alpha = multipliedAlphaValue(qAlpha(curCol)); + if (alpha != qAlpha(curCol)) + colorTable[i] = qRgba(qRed(curCol), qGreen(curCol), qBlue(curCol), alpha); + } + } + image.setColorTable(colorTable); + } + + return image; +} + +int QCustom3DVolumePrivate::multipliedAlphaValue(int alpha) +{ + int modifiedAlpha = alpha; + if (!m_preserveOpacity || alpha != 255) { + modifiedAlpha = int(m_alphaMultiplier * float(alpha)); + modifiedAlpha = qMin(modifiedAlpha, 255); + } + return modifiedAlpha; +} + QCustom3DVolume *QCustom3DVolumePrivate::qptr() { return static_cast(q_ptr); diff --git a/src/datavisualization/data/qcustom3dvolume.h b/src/datavisualization/data/qcustom3dvolume.h index 00733d17..2f95fa5d 100644 --- a/src/datavisualization/data/qcustom3dvolume.h +++ b/src/datavisualization/data/qcustom3dvolume.h @@ -40,7 +40,8 @@ class QT_DATAVISUALIZATION_EXPORT QCustom3DVolume : public QCustom3DItem Q_PROPERTY(QVector colorTable READ colorTable WRITE setColorTable NOTIFY colorTableChanged) Q_PROPERTY(QVector *textureData READ textureData WRITE setTextureData NOTIFY textureDataChanged) Q_PROPERTY(QImage::Format textureFormat READ textureFormat WRITE setTextureFormat NOTIFY textureFormatChanged) - + Q_PROPERTY(float alphaMultiplier READ alphaMultiplier WRITE setAlphaMultiplier NOTIFY alphaMultiplierChanged) + Q_PROPERTY(bool preserveOpacity READ preserveOpacity WRITE setPreserveOpacity NOTIFY preserveOpacityChanged) public: explicit QCustom3DVolume(QObject *parent = 0); @@ -80,6 +81,11 @@ public: void setTextureFormat(QImage::Format format); QImage::Format textureFormat() const; + void setAlphaMultiplier(float mult); + float alphaMultiplier() const; + void setPreserveOpacity(bool enable); + bool preserveOpacity() const; + QImage renderSlice(Qt::Axis axis, int index); signals: @@ -92,6 +98,8 @@ signals: void colorTableChanged(); void textureDataChanged(QVector *data); void textureFormatChanged(QImage::Format format); + void alphaMultiplierChanged(float mult); + void preserveOpacityChanged(bool enabled); protected: QCustom3DVolumePrivate *dptr(); diff --git a/src/datavisualization/data/qcustom3dvolume_p.h b/src/datavisualization/data/qcustom3dvolume_p.h index 69dd1eb2..b83e27fb 100644 --- a/src/datavisualization/data/qcustom3dvolume_p.h +++ b/src/datavisualization/data/qcustom3dvolume_p.h @@ -40,13 +40,15 @@ struct QCustomVolumeDirtyBitField { bool colorTableDirty : 1; bool textureDataDirty : 1; bool textureFormatDirty : 1; + bool alphaDirty : 1; QCustomVolumeDirtyBitField() : textureDimensionsDirty(false), sliceIndicesDirty(false), colorTableDirty(false), textureDataDirty(false), - textureFormatDirty(false) + textureFormatDirty(false), + alphaDirty(false) { } }; @@ -64,6 +66,7 @@ public: virtual ~QCustom3DVolumePrivate(); void resetDirtyBits(); + QImage renderSlice(Qt::Axis axis, int index); QCustom3DVolume *qptr(); @@ -79,9 +82,14 @@ public: QVector m_colorTable; QVector *m_textureData; + float m_alphaMultiplier; + bool m_preserveOpacity; + QCustomVolumeDirtyBitField m_dirtyBitsVolume; private: + int multipliedAlphaValue(int alpha); + friend class QCustom3DVolume; }; diff --git a/src/datavisualization/engine/abstract3drenderer.cpp b/src/datavisualization/engine/abstract3drenderer.cpp index 8eb1d2ce..37688beb 100644 --- a/src/datavisualization/engine/abstract3drenderer.cpp +++ b/src/datavisualization/engine/abstract3drenderer.cpp @@ -967,6 +967,8 @@ CustomRenderItem *Abstract3DRenderer::addCustomItem(QCustom3DItem *item) newItem->setSliceIndexX(volumeItem->sliceIndexX()); newItem->setSliceIndexY(volumeItem->sliceIndexY()); newItem->setSliceIndexZ(volumeItem->sliceIndexZ()); + newItem->setAlphaMultiplier(volumeItem->alphaMultiplier()); + newItem->setPreserveOpacity(volumeItem->preserveOpacity()); #endif } newItem->setScaling(scaling); @@ -1114,6 +1116,11 @@ void Abstract3DRenderer::updateCustomItem(CustomRenderItem *renderItem) renderItem->setSliceIndexZ(volumeItem->sliceIndexZ()); volumeItem->dptr()->m_dirtyBitsVolume.sliceIndicesDirty = false; } + if (volumeItem->dptr()->m_dirtyBitsVolume.alphaDirty) { + renderItem->setAlphaMultiplier(volumeItem->alphaMultiplier()); + renderItem->setPreserveOpacity(volumeItem->preserveOpacity()); + volumeItem->dptr()->m_dirtyBitsVolume.alphaDirty = false; + } #endif } } @@ -1292,6 +1299,9 @@ void Abstract3DRenderer::drawCustomItems(RenderingState state, item->colorTable().constData(), 256); } shader->setUniformValue(shader->color8Bit(), color8Bit); + shader->setUniformValue(shader->alphaMultiplier(), item->alphaMultiplier()); + shader->setUniformValue(shader->preserveOpacity(), + item->preserveOpacity() ? 1 : 0); if (shader == volumeSliceShader) { QVector3D slices((float(item->sliceIndexX()) + 0.5f) / float(item->textureWidth()) * 2.0 - 1.0, diff --git a/src/datavisualization/engine/shaders/texture3d.frag b/src/datavisualization/engine/shaders/texture3d.frag index 90876596..1192ae85 100644 --- a/src/datavisualization/engine/shaders/texture3d.frag +++ b/src/datavisualization/engine/shaders/texture3d.frag @@ -8,6 +8,8 @@ uniform highp vec4 colorIndex[256]; uniform highp int color8Bit; uniform highp vec3 textureDimensions; uniform highp int sampleCount; // This is the maximum sample count +uniform highp float alphaMultiplier; +uniform highp int preserveOpacity; const highp float alphaThreshold = 0.0001; @@ -69,7 +71,7 @@ void main() { // Adjust alpha multiplier according to the step size to get uniform alpha effect // regardless of the ray angle. - highp float alphaMultiplier = stepSize / (1.0 / sampleCount); + highp float totalAlphaMultiplier = (stepSize / (1.0 / sampleCount)) * alphaMultiplier; highp vec4 curColor = vec4(0, 0, 0, 0); highp vec3 curRgb = vec3(0, 0, 0); @@ -81,10 +83,12 @@ void main() { if (color8Bit != 0) curColor = colorIndex[int(curColor.r * 255.0)]; - if (curColor.a == 1.0) + // Unless we have explicit alpha multiplier, we want to preserve opacity anyway + if (curColor.a == 1.0 && (preserveOpacity != 0 || alphaMultiplier == 1.0)) curAlpha = 1.0; else - curAlpha = clamp(curColor.a * alphaMultiplier, 0.0, 1.0); + curAlpha = clamp(curColor.a * totalAlphaMultiplier, 0.0, 1.0); + if (curAlpha > alphaThreshold) { curRgb = curColor.rgb * curAlpha * (1.0 - totalAlpha); destColor.rgb += curRgb; @@ -96,6 +100,10 @@ void main() { } } + // Brighten up the final color if there is some transparency left + if (totalAlpha > alphaThreshold && totalAlpha < 1.0) + destColor *= 1.0 / totalAlpha; + destColor.a = totalAlpha; gl_FragColor = clamp(destColor, 0.0, 1.0); } diff --git a/src/datavisualization/engine/shaders/texture3dslice.frag b/src/datavisualization/engine/shaders/texture3dslice.frag index 8870b26d..3d4c9030 100644 --- a/src/datavisualization/engine/shaders/texture3dslice.frag +++ b/src/datavisualization/engine/shaders/texture3dslice.frag @@ -7,6 +7,8 @@ uniform highp vec3 cameraPositionRelativeToModel; uniform highp vec3 volumeSliceIndices; uniform highp vec4 colorIndex[256]; uniform highp int color8Bit; +uniform highp float alphaMultiplier; +uniform highp int preserveOpacity; const highp vec3 xPlaneNormal = vec3(1.0, 0, 0); const highp vec3 yPlaneNormal = vec3(0, 1.0, 0); @@ -76,47 +78,67 @@ void main() { } highp vec4 destColor = vec4(0.0, 0.0, 0.0, 0.0); + highp vec4 curColor = vec4(0.0, 0.0, 0.0, 0.0); highp float totalAlpha = 0.0; highp vec3 curRgb = vec3(0, 0, 0); + highp float curAlpha = 0.0; // Convert intersection to texture coords if (firstD <= tFar) { highp vec3 firstTex = rayStart + rayDir * firstD; firstTex = 0.5 * (firstTex + 1.0); - highp vec4 firstColor = texture3D(textureSampler, firstTex); + curColor = texture3D(textureSampler, firstTex); if (color8Bit != 0) - firstColor = colorIndex[int(firstColor.r * 255.0)]; + curColor = colorIndex[int(curColor.r * 255.0)]; - if (firstColor.a > alphaThreshold) { - destColor.rgb = firstColor.rgb * firstColor.a; - totalAlpha = firstColor.a; + if (curColor.a > alphaThreshold) { + curAlpha = curColor.a; + if (curColor.a == 1.0 && preserveOpacity != 0) + curAlpha = 1.0; + else + curAlpha = clamp(curColor.a * alphaMultiplier, 0.0, 1.0); + destColor.rgb = curColor.rgb * curAlpha; + totalAlpha = curAlpha; } if (secondD <= tFar && totalAlpha < 1.0) { highp vec3 secondTex = rayStart + rayDir * secondD; secondTex = 0.5 * (secondTex + 1.0); - highp vec4 secondColor = texture3D(textureSampler, secondTex); + curColor = texture3D(textureSampler, secondTex); if (color8Bit != 0) - secondColor = colorIndex[int(secondColor.r * 255.0)]; - if (secondColor.a > alphaThreshold) { - curRgb = secondColor.rgb * secondColor.a * (1.0 - totalAlpha); + curColor = colorIndex[int(curColor.r * 255.0)]; + if (curColor.a > alphaThreshold) { + if (curColor.a == 1.0 && preserveOpacity != 0) + curAlpha = 1.0; + else + curAlpha = clamp(curColor.a * alphaMultiplier, 0.0, 1.0); + curRgb = curColor.rgb * curAlpha * (1.0 - totalAlpha); destColor.rgb += curRgb; - totalAlpha += secondColor.a; + totalAlpha += curAlpha; } if (thirdD <= tFar && totalAlpha < 1.0) { highp vec3 thirdTex = rayStart + rayDir * thirdD; thirdTex = 0.5 * (thirdTex + 1.0); - highp vec4 thirdColor = texture3D(textureSampler, thirdTex); - if (color8Bit != 0) - thirdColor = colorIndex[int(thirdColor.r * 255.0)]; - if (thirdColor.a > alphaThreshold) { - curRgb = thirdColor.rgb * thirdColor.a * (1.0 - totalAlpha); + curColor = texture3D(textureSampler, thirdTex); + if (curColor.a > alphaThreshold) { + if (color8Bit != 0) + curColor = colorIndex[int(curColor.r * 255.0)]; + if (curColor.a == 1.0 && preserveOpacity != 0) + curAlpha = 1.0; + else + curAlpha = clamp(curColor.a * alphaMultiplier, 0.0, 1.0); + curRgb = curColor.rgb * curAlpha * (1.0 - totalAlpha); destColor.rgb += curRgb; - totalAlpha += thirdColor.a; + totalAlpha += curAlpha; } } } } + + // Brighten up the final color if there is some transparency left + if (totalAlpha > alphaThreshold && totalAlpha < 1.0) + destColor *= 1.0 / totalAlpha; + destColor.a = totalAlpha; gl_FragColor = clamp(destColor, 0.0, 1.0); } diff --git a/src/datavisualization/utils/shaderhelper.cpp b/src/datavisualization/utils/shaderhelper.cpp index 3361638a..9d1ad0d9 100644 --- a/src/datavisualization/utils/shaderhelper.cpp +++ b/src/datavisualization/utils/shaderhelper.cpp @@ -99,6 +99,8 @@ void ShaderHelper::initialize() m_color8BitUniform = m_program->uniformLocation("color8Bit"); m_textureDimensionsUniform = m_program->uniformLocation("textureDimensions"); m_sampleCountUniform = m_program->uniformLocation("sampleCount"); + m_alphaMultiplierUniform = m_program->uniformLocation("alphaMultiplier"); + m_preserveOpacityUniform = m_program->uniformLocation("preserveOpacity"); m_initialized = true; } @@ -308,6 +310,20 @@ GLuint ShaderHelper::sampleCount() return m_sampleCountUniform; } +GLuint ShaderHelper::alphaMultiplier() +{ + if (!m_initialized) + qFatal("Shader not initialized"); + return m_alphaMultiplierUniform; +} + +GLuint ShaderHelper::preserveOpacity() +{ + if (!m_initialized) + qFatal("Shader not initialized"); + return m_preserveOpacityUniform; +} + GLuint ShaderHelper::posAtt() { if (!m_initialized) diff --git a/src/datavisualization/utils/shaderhelper_p.h b/src/datavisualization/utils/shaderhelper_p.h index bc7609bb..ac815447 100644 --- a/src/datavisualization/utils/shaderhelper_p.h +++ b/src/datavisualization/utils/shaderhelper_p.h @@ -80,6 +80,8 @@ class ShaderHelper GLuint color8Bit(); GLuint textureDimensions(); GLuint sampleCount(); + GLuint alphaMultiplier(); + GLuint preserveOpacity(); GLuint posAtt(); GLuint uvAtt(); @@ -120,6 +122,8 @@ class ShaderHelper GLuint m_color8BitUniform; GLuint m_textureDimensionsUniform; GLuint m_sampleCountUniform; + GLuint m_alphaMultiplierUniform; + GLuint m_preserveOpacityUniform; GLboolean m_initialized; }; diff --git a/tests/volumetrictest/volumetrictest.cpp b/tests/volumetrictest/volumetrictest.cpp index 3485dd24..554373cb 100644 --- a/tests/volumetrictest/volumetrictest.cpp +++ b/tests/volumetrictest/volumetrictest.cpp @@ -317,7 +317,7 @@ void VolumetricModifier::createAnotherVolume() QImage logo; logo.load(QStringLiteral(":/logo.png")); - //logo = logo.convertToFormat(QImage::Format_ARGB8555_Premultiplied); + logo = logo.convertToFormat(QImage::Format_ARGB8555_Premultiplied); qDebug() << "second image dimensions:" << logo.width() << logo.height() << logo.byteCount() << (logo.width() * logo.height()) << logo.bytesPerLine(); @@ -347,6 +347,8 @@ void VolumetricModifier::createAnotherVolume() // Change one picture using subtexture replacement QImage flipped = logo.mirrored(); m_volumeItem2->setSubTextureData(100, flipped); + m_volumeItem2->setAlphaMultiplier(0.2f); + m_volumeItem2->setPreserveOpacity(false); } void VolumetricModifier::createYetAnotherVolume() -- cgit v1.2.3 From fcac33d0ee25115808ca43810cf6a3438e2da227 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomi=20Korpip=C3=A4=C3=A4?= Date: Tue, 26 Aug 2014 11:26:06 +0300 Subject: Reflection API added Task-number: QTRD-3287 Change-Id: I6c06b8fe025e0f1f87be00be906cab0e1f18a19f Reviewed-by: Miikka Heikkinen --- ...tdatavisualization-qml-abstractdeclarative.qdoc | 25 ++- .../engine/abstract3dcontroller.cpp | 43 +++++ .../engine/abstract3dcontroller_p.h | 15 +- .../engine/abstract3drenderer.cpp | 78 ++++----- .../engine/abstract3drenderer_p.h | 15 +- src/datavisualization/engine/bars3drenderer.cpp | 178 +++++++++------------ src/datavisualization/engine/bars3drenderer_p.h | 11 +- src/datavisualization/engine/qabstract3dgraph.cpp | 46 ++++++ src/datavisualization/engine/qabstract3dgraph.h | 10 ++ src/datavisualizationqml2/abstractdeclarative.cpp | 20 +++ src/datavisualizationqml2/abstractdeclarative_p.h | 10 ++ tests/barstest/chart.cpp | 11 ++ tests/barstest/chart.h | 2 + tests/barstest/main.cpp | 19 ++- tests/qmlcamera/qml/qmlcamera/main.qml | 18 ++- 15 files changed, 327 insertions(+), 174 deletions(-) diff --git a/src/datavisualization/doc/src/qtdatavisualization-qml-abstractdeclarative.qdoc b/src/datavisualization/doc/src/qtdatavisualization-qml-abstractdeclarative.qdoc index c83beb74..dfa65877 100644 --- a/src/datavisualization/doc/src/qtdatavisualization-qml-abstractdeclarative.qdoc +++ b/src/datavisualization/doc/src/qtdatavisualization-qml-abstractdeclarative.qdoc @@ -340,7 +340,7 @@ /*! * \qmlproperty AbstractGraph3D.OptimizationHints AbstractGraph3D::optimizationHints - * \since Qt Data Visualization 1.1 + * \since QtDataVisualization 1.1 * * Defines if the rendering optimization is default or static. Default mode provides the full feature set at * reasonable performance. Static is a beta level feature and currently supports only a subset of the @@ -351,3 +351,26 @@ * with massive data sets is not advisable. * Defaults to \c{OptimizationDefault} */ + +/*! + * \qmlproperty bool AbstractGraph3D::reflection + * \since QtDataVisualization 1.2 + * + * Sets floor reflections on or off. Defaults to \c{false}. + * + * \note Affects only Bars3D. + * + * \sa reflectivity + */ + +/*! + * \qmlproperty real AbstractGraph3D::reflectivity + * \since QtDataVisualization 1.2 + * + * Adjusts floor reflectivity, larger number being more reflective. Valid range is \c{[0...1]}. + * Defaults to \c{0.5}. + * + * \note Affects only Bars3D. + * + * \sa reflection + */ diff --git a/src/datavisualization/engine/abstract3dcontroller.cpp b/src/datavisualization/engine/abstract3dcontroller.cpp index 52ab853d..feb028cc 100644 --- a/src/datavisualization/engine/abstract3dcontroller.cpp +++ b/src/datavisualization/engine/abstract3dcontroller.cpp @@ -40,6 +40,8 @@ Abstract3DController::Abstract3DController(QRect initialViewport, Q3DScene *scen m_aspectRatio(2.0), m_horizontalAspectRatio(0.0), m_optimizationHints(QAbstract3DGraph::OptimizationDefault), + m_reflectionEnabled(false), + m_reflectivity(0.5), m_scene(scene), m_activeInputHandler(0), m_axisX(0), @@ -215,6 +217,17 @@ void Abstract3DController::synchDataToRenderer() m_changeTracker.optimizationHintChanged = false; } + if (m_changeTracker.reflectionChanged) { + m_renderer->m_reflectionEnabled = m_reflectionEnabled; + m_changeTracker.reflectionChanged = false; + } + + if (m_changeTracker.reflectivityChanged) { + // Invert value to match functionality to the property description + m_renderer->m_reflectivity = -(m_reflectivity - 1.0); + m_changeTracker.reflectivityChanged = false; + } + if (m_changeTracker.axisXFormatterChanged) { m_changeTracker.axisXFormatterChanged = false; if (m_axisX->type() & QAbstract3DAxis::AxisTypeValue) { @@ -1558,6 +1571,36 @@ qreal Abstract3DController::horizontalAspectRatio() const return m_horizontalAspectRatio; } +void Abstract3DController::setReflection(bool enable) +{ + if (m_reflectionEnabled != enable) { + m_reflectionEnabled = enable; + m_changeTracker.reflectionChanged = true; + emit reflectionChanged(m_reflectionEnabled); + emitNeedRender(); + } +} + +bool Abstract3DController::reflection() const +{ + return m_reflectionEnabled; +} + +void Abstract3DController::setReflectivity(qreal reflectivity) +{ + if (m_reflectivity != reflectivity) { + m_reflectivity = reflectivity; + m_changeTracker.reflectivityChanged = true; + emit reflectivityChanged(m_reflectivity); + emitNeedRender(); + } +} + +qreal Abstract3DController::reflectivity() const +{ + return m_reflectivity; +} + void Abstract3DController::setPolar(bool enable) { if (enable != m_isPolar) { diff --git a/src/datavisualization/engine/abstract3dcontroller_p.h b/src/datavisualization/engine/abstract3dcontroller_p.h index f6cfb057..3397fc62 100644 --- a/src/datavisualization/engine/abstract3dcontroller_p.h +++ b/src/datavisualization/engine/abstract3dcontroller_p.h @@ -93,6 +93,8 @@ struct Abstract3DChangeBitField { bool axisZTitleFixedChanged : 1; bool polarChanged : 1; bool radialLabelOffsetChanged : 1; + bool reflectionChanged : 1; + bool reflectivityChanged : 1; Abstract3DChangeBitField() : themeChanged(true), @@ -139,7 +141,9 @@ struct Abstract3DChangeBitField { axisYTitleFixedChanged(true), axisZTitleFixedChanged(true), polarChanged(true), - radialLabelOffsetChanged(true) + radialLabelOffsetChanged(true), + reflectionChanged(true), + reflectivityChanged(true) { } }; @@ -165,6 +169,8 @@ private: qreal m_aspectRatio; qreal m_horizontalAspectRatio; QAbstract3DGraph::OptimizationHints m_optimizationHints; + bool m_reflectionEnabled; + qreal m_reflectivity; protected: Q3DScene *m_scene; @@ -281,6 +287,11 @@ public: void setHorizontalAspectRatio(qreal ratio); qreal horizontalAspectRatio() const; + void setReflection(bool enable); + bool reflection() const; + void setReflectivity(qreal reflectivity); + qreal reflectivity() const; + void setPolar(bool enable); bool isPolar() const; void setRadialLabelOffset(float offset); @@ -365,6 +376,8 @@ signals: void optimizationHintsChanged(QAbstract3DGraph::OptimizationHints hints); void polarChanged(bool enabled); void radialLabelOffsetChanged(float offset); + void reflectionChanged(bool enabled); + void reflectivityChanged(qreal reflectivity); protected: virtual QAbstract3DAxis *createDefaultAxis(QAbstract3DAxis::AxisOrientation orientation); diff --git a/src/datavisualization/engine/abstract3drenderer.cpp b/src/datavisualization/engine/abstract3drenderer.cpp index 37688beb..7cf7f020 100644 --- a/src/datavisualization/engine/abstract3drenderer.cpp +++ b/src/datavisualization/engine/abstract3drenderer.cpp @@ -83,7 +83,9 @@ Abstract3DRenderer::Abstract3DRenderer(Abstract3DController *controller) m_zFlipRotation(QQuaternion::fromAxisAndAngle(0.0f, 0.0f, 1.0f, -180.0f)), m_vBackgroundMargin(0.1f), m_hBackgroundMargin(0.1f), - m_oldCameraTarget(QVector3D(2000.0f, 2000.0f, 2000.0f)) // Just random invalid target + m_oldCameraTarget(QVector3D(2000.0f, 2000.0f, 2000.0f)), // Just random invalid target + m_reflectionEnabled(false), + m_reflectivity(0.5) { QObject::connect(m_drawer, &Drawer::drawerChanged, this, &Abstract3DRenderer::updateTextures); QObject::connect(this, &Abstract3DRenderer::needRender, controller, @@ -1049,7 +1051,7 @@ void Abstract3DRenderer::updateCustomItem(CustomRenderItem *renderItem) } } else #if !defined(QT_OPENGL_ES_2) - if (!item->d_ptr->m_isVolumeItem) + if (!item->d_ptr->m_isVolumeItem) #endif { renderItem->setBlendNeeded(textureImage.hasAlphaChannel()); @@ -1097,10 +1099,10 @@ void Abstract3DRenderer::updateCustomItem(CustomRenderItem *renderItem) GLuint oldTexture = renderItem->texture(); m_textureHelper->deleteTexture(&oldTexture); GLuint texture = m_textureHelper->create3DTexture(volumeItem->textureData(), - volumeItem->textureWidth(), - volumeItem->textureHeight(), - volumeItem->textureDepth(), - volumeItem->textureFormat()); + volumeItem->textureWidth(), + volumeItem->textureHeight(), + volumeItem->textureDepth(), + volumeItem->textureFormat()); renderItem->setTexture(texture); renderItem->setTextureWidth(volumeItem->textureWidth()); renderItem->setTextureHeight(volumeItem->textureHeight()); @@ -1134,7 +1136,6 @@ void Abstract3DRenderer::updateCustomItemPositions() } } -#ifdef USE_REFLECTIONS void Abstract3DRenderer::drawCustomItems(RenderingState state, ShaderHelper *regularShader, ShaderHelper *volumeShader, @@ -1145,17 +1146,6 @@ void Abstract3DRenderer::drawCustomItems(RenderingState state, GLuint depthTexture, GLfloat shadowQuality, GLfloat reflection) -#else -void Abstract3DRenderer::drawCustomItems(RenderingState state, - ShaderHelper *regularShader, - ShaderHelper *volumeShader, - ShaderHelper *volumeSliceShader, - const QMatrix4x4 &viewMatrix, - const QMatrix4x4 &projectionViewMatrix, - const QMatrix4x4 &depthProjectionViewMatrix, - GLuint depthTexture, - GLfloat shadowQuality) -#endif { #if defined(QT_OPENGL_ES_2) Q_UNUSED(volumeShader) @@ -1205,36 +1195,36 @@ void Abstract3DRenderer::drawCustomItems(RenderingState state, * QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, -camRotationY); } -#ifdef USE_REFLECTIONS - if (reflection < 0.0f) { - if (item->itemPointer()->d_ptr->m_isLabelItem) - continue; - else - glCullFace(GL_FRONT); - } else { - glCullFace(GL_BACK); - } - QVector3D trans = item->translation(); - trans.setY(reflection * trans.y()); - modelMatrix.translate(trans); - if (reflection < 0.0f) { - QQuaternion mirror = QQuaternion(rotation.scalar(), - -rotation.x(), rotation.y(), -rotation.z()); - modelMatrix.rotate(mirror); - itModelMatrix.rotate(mirror); + if (m_reflectionEnabled) { + if (reflection < 0.0f) { + if (item->itemPointer()->d_ptr->m_isLabelItem) + continue; + else + glCullFace(GL_FRONT); + } else { + glCullFace(GL_BACK); + } + QVector3D trans = item->translation(); + trans.setY(reflection * trans.y()); + modelMatrix.translate(trans); + if (reflection < 0.0f) { + QQuaternion mirror = QQuaternion(rotation.scalar(), + -rotation.x(), rotation.y(), -rotation.z()); + modelMatrix.rotate(mirror); + itModelMatrix.rotate(mirror); + } else { + modelMatrix.rotate(rotation); + itModelMatrix.rotate(rotation); + } + QVector3D scale = item->scaling(); + scale.setY(reflection * scale.y()); + modelMatrix.scale(scale); } else { + modelMatrix.translate(item->translation()); modelMatrix.rotate(rotation); + modelMatrix.scale(item->scaling()); itModelMatrix.rotate(rotation); } - QVector3D scale = item->scaling(); - scale.setY(reflection * scale.y()); - modelMatrix.scale(scale); -#else - modelMatrix.translate(item->translation()); - modelMatrix.rotate(rotation); - modelMatrix.scale(item->scaling()); - itModelMatrix.rotate(rotation); -#endif if (!item->isFacingCamera()) itModelMatrix.scale(item->scaling()); MVPMatrix = projectionViewMatrix * modelMatrix; diff --git a/src/datavisualization/engine/abstract3drenderer_p.h b/src/datavisualization/engine/abstract3drenderer_p.h index 7874a1ad..1e815463 100644 --- a/src/datavisualization/engine/abstract3drenderer_p.h +++ b/src/datavisualization/engine/abstract3drenderer_p.h @@ -29,8 +29,6 @@ #ifndef ABSTRACT3DRENDERER_P_H #define ABSTRACT3DRENDERER_P_H -//#define USE_REFLECTIONS // Bars only test version (only floor reflects) - #include #include "datavisualizationglobal_p.h" @@ -147,21 +145,13 @@ public: void setSelectionLabel(const QString &label); QString &selectionLabel(); -#ifdef USE_REFLECTIONS void drawCustomItems(RenderingState state, ShaderHelper *regularShader, ShaderHelper *volumeShader, ShaderHelper *volumeSliceShader, const QMatrix4x4 &viewMatrix, const QMatrix4x4 &projectionViewMatrix, const QMatrix4x4 &depthProjectionViewMatrix, GLuint depthTexture, GLfloat shadowQuality, GLfloat reflection = 1.0f); -#else - void drawCustomItems(RenderingState state, ShaderHelper *regularShader, - ShaderHelper *volumeShader, ShaderHelper *volumeSliceShader, - const QMatrix4x4 &viewMatrix, - const QMatrix4x4 &projectionViewMatrix, - const QMatrix4x4 &depthProjectionViewMatrix, - GLuint depthTexture, GLfloat shadowQuality); -#endif + QVector4D indexToSelectionColor(GLint index); void calculatePolarXZ(const QVector3D &dataPos, float &x, float &z) const; @@ -285,6 +275,9 @@ protected: QVector3D m_oldCameraTarget; + bool m_reflectionEnabled; + qreal m_reflectivity; + private: friend class Abstract3DController; }; diff --git a/src/datavisualization/engine/bars3drenderer.cpp b/src/datavisualization/engine/bars3drenderer.cpp index 085b881e..7e49c0b3 100644 --- a/src/datavisualization/engine/bars3drenderer.cpp +++ b/src/datavisualization/engine/bars3drenderer.cpp @@ -1043,12 +1043,11 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) shadowOffset = -0.015f; } -#ifdef USE_REFLECTIONS - if (m_yFlipped && item.height() > 0.0 - || !m_yFlipped && item.height() < 0.0) { + if (m_reflectionEnabled && (m_yFlipped && item.height() > 0.0 + || !m_yFlipped && item.height() < 0.0)) { continue; } -#endif + QMatrix4x4 modelMatrix; QMatrix4x4 MVPMatrix; @@ -1205,61 +1204,65 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) m_primarySubViewport.height()); } -#ifdef USE_REFLECTIONS - // - // Draw reflections - // - glDisable(GL_DEPTH_TEST); - glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); - glEnable(GL_STENCIL_TEST); - glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE); - glStencilFunc(GL_ALWAYS, 1, 0xffffffff); + if (m_reflectionEnabled) { + // + // Draw reflections + // + glDisable(GL_DEPTH_TEST); + glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); + glEnable(GL_STENCIL_TEST); + glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE); + glStencilFunc(GL_ALWAYS, 1, 0xffffffff); - // Draw background stencil - drawBackground(backgroundRotation, depthProjectionViewMatrix, projectionViewMatrix, viewMatrix); + // Draw background stencil + drawBackground(backgroundRotation, depthProjectionViewMatrix, projectionViewMatrix, + viewMatrix); - glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); - glEnable(GL_DEPTH_TEST); + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + glEnable(GL_DEPTH_TEST); - glStencilFunc(GL_EQUAL, 1, 0xffffffff); - glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); + glStencilFunc(GL_EQUAL, 1, 0xffffffff); + glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); - // Set light - QVector3D reflectionLightPos = lightPos; - reflectionLightPos.setY(-(lightPos.y())); - m_cachedScene->activeLight()->setPosition(reflectionLightPos); + // Set light + QVector3D reflectionLightPos = lightPos; + reflectionLightPos.setY(-(lightPos.y())); + m_cachedScene->activeLight()->setPosition(reflectionLightPos); - // Draw bar reflections - (void)drawBars(&selectedBar, depthProjectionViewMatrix, - projectionViewMatrix, viewMatrix, - startRow, stopRow, stepRow, - startBar, stopBar, stepBar, -1.0f); + // Draw bar reflections + (void)drawBars(&selectedBar, depthProjectionViewMatrix, + projectionViewMatrix, viewMatrix, + startRow, stopRow, stepRow, + startBar, stopBar, stepBar, -1.0f); - Abstract3DRenderer::drawCustomItems(RenderingNormal, m_customItemShader, viewMatrix, - projectionViewMatrix, depthProjectionViewMatrix, - m_depthTexture, m_shadowQualityToShader, -1.0f); + Abstract3DRenderer::drawCustomItems(RenderingNormal, m_customItemShader, + m_volumeTextureShader, m_volumeTextureSliceShader, + viewMatrix, projectionViewMatrix, + depthProjectionViewMatrix, m_depthTexture, + m_shadowQualityToShader, -1.0f); - // Reset light - m_cachedScene->activeLight()->setPosition(lightPos); + // Reset light + m_cachedScene->activeLight()->setPosition(lightPos); - glDisable(GL_STENCIL_TEST); + glDisable(GL_STENCIL_TEST); - glCullFace(GL_BACK); -#endif + glCullFace(GL_BACK); + } // // Draw the real scene // // Draw background -#ifdef USE_REFLECTIONS - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - drawBackground(backgroundRotation, depthProjectionViewMatrix, projectionViewMatrix, viewMatrix, - 0.5f); - glDisable(GL_BLEND); -#else - drawBackground(backgroundRotation, depthProjectionViewMatrix, projectionViewMatrix, viewMatrix); -#endif + if (m_reflectionEnabled) { + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + drawBackground(backgroundRotation, depthProjectionViewMatrix, projectionViewMatrix, + viewMatrix, true); + glDisable(GL_BLEND); + } else { + drawBackground(backgroundRotation, depthProjectionViewMatrix, projectionViewMatrix, + viewMatrix); + } // Draw bars bool barSelectionFound = drawBars(&selectedBar, depthProjectionViewMatrix, @@ -1320,19 +1323,11 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) m_selectionDirty = false; } -#ifdef USE_REFLECTIONS bool Bars3DRenderer::drawBars(BarRenderItem **selectedBar, const QMatrix4x4 &depthProjectionViewMatrix, const QMatrix4x4 &projectionViewMatrix, const QMatrix4x4 &viewMatrix, GLint startRow, GLint stopRow, GLint stepRow, GLint startBar, GLint stopBar, GLint stepBar, GLfloat reflection) -#else -bool Bars3DRenderer::drawBars(BarRenderItem **selectedBar, - const QMatrix4x4 &depthProjectionViewMatrix, - const QMatrix4x4 &projectionViewMatrix, const QMatrix4x4 &viewMatrix, - GLint startRow, GLint stopRow, GLint stepRow, - GLint startBar, GLint stopBar, GLint stepBar) -#endif { QVector3D lightPos = m_cachedScene->activeLight()->position(); QVector4D lightColor = Utils::vectorFromColor(m_cachedTheme->lightColor()); @@ -1437,11 +1432,8 @@ bool Bars3DRenderer::drawBars(BarRenderItem **selectedBar, BarRenderItemRow &renderRow = renderArray[row]; for (int bar = startBar; bar != stopBar; bar += stepBar) { BarRenderItem &item = renderRow[bar]; -#ifdef USE_REFLECTIONS - if (reflection * item.height() < 0) -#else - if (item.height() < 0) -#endif + float adjustedHeight = reflection * item.height(); + if (adjustedHeight < 0) glCullFace(GL_FRONT); else glCullFace(GL_BACK); @@ -1453,17 +1445,10 @@ bool Bars3DRenderer::drawBars(BarRenderItem **selectedBar, GLfloat colPos = (bar + seriesPos) * (m_cachedBarSpacing.width()); GLfloat rowPos = (row + 0.5f) * (m_cachedBarSpacing.height()); -#ifdef USE_REFLECTIONS modelMatrix.translate((colPos - m_rowWidth) / m_scaleFactor, - reflection * item.height(), + adjustedHeight, (m_columnDepth - rowPos) / m_scaleFactor); - modelScaler.setY(reflection * item.height()); -#else - modelMatrix.translate((colPos - m_rowWidth) / m_scaleFactor, - item.height(), - (m_columnDepth - rowPos) / m_scaleFactor); - modelScaler.setY(item.height()); -#endif + modelScaler.setY(adjustedHeight); if (!seriesRotation.isIdentity() || !item.rotation().isIdentity()) { QQuaternion totalRotation = seriesRotation * item.rotation(); modelMatrix.rotate(totalRotation); @@ -1579,16 +1564,15 @@ bool Bars3DRenderer::drawBars(BarRenderItem **selectedBar, } } -#ifdef USE_REFLECTIONS - // Skip drawing of 0-height bars and reflections of bars on the "wrong side" - if (item.height() != 0 - && (reflection == 1.0f || (reflection != 1.0f - && (m_yFlipped && item.height() < 0.0) - || (!m_yFlipped && item.height() > 0.0)))) { -#else - // Skip drawing of 0-height bars - if (item.height() != 0) { -#endif + if (item.height() == 0) { + continue; + } else if ((m_reflectionEnabled + && (reflection == 1.0f + || (reflection != 1.0f + && (m_yFlipped && item.height() < 0.0) + || (!m_yFlipped && item.height() > 0.0)))) + || !m_reflectionEnabled) { + // Skip drawing of 0-height bars and reflections of bars on the "wrong side" // Set shader bindings barShader->setUniformValue(barShader->model(), modelMatrix); barShader->setUniformValue(barShader->nModel(), @@ -1602,12 +1586,9 @@ bool Bars3DRenderer::drawBars(BarRenderItem **selectedBar, } #if !defined(QT_OPENGL_ES_2) -#ifdef USE_REFLECTIONS - if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone - && reflection == 1.0f) { -#else - if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { -#endif + if ((m_reflectionEnabled && reflection == 1.0f + && m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) + || m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { // Set shadow shader bindings QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix; barShader->setUniformValue(barShader->shadowQ(), @@ -1626,14 +1607,11 @@ bool Bars3DRenderer::drawBars(BarRenderItem **selectedBar, #endif { // Set shadowless shader bindings -#ifdef USE_REFLECTIONS - if (reflection != 1.0f + if (m_reflectionEnabled && reflection != 1.0f && m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { barShader->setUniformValue(barShader->lightS(), adjustedLightStrength); - } else -#endif - { + } else { barShader->setUniformValue(barShader->lightS(), lightStrength); } @@ -1653,17 +1631,10 @@ bool Bars3DRenderer::drawBars(BarRenderItem **selectedBar, return barSelectionFound; } -#ifdef USE_REFLECTIONS -void Bars3DRenderer::drawBackground(GLfloat backgroundRotation, - const QMatrix4x4 &depthProjectionViewMatrix, - const QMatrix4x4 &projectionViewMatrix, - const QMatrix4x4 &viewMatrix, GLfloat reflection) -#else void Bars3DRenderer::drawBackground(GLfloat backgroundRotation, const QMatrix4x4 &depthProjectionViewMatrix, const QMatrix4x4 &projectionViewMatrix, - const QMatrix4x4 &viewMatrix) -#endif + const QMatrix4x4 &viewMatrix, bool reflectingDraw) { QVector3D lightPos = m_cachedScene->activeLight()->position(); QVector4D lightColor = Utils::vectorFromColor(m_cachedTheme->lightColor()); @@ -1680,9 +1651,8 @@ void Bars3DRenderer::drawBackground(GLfloat backgroundRotation, QVector3D backgroundScaler(m_xScaleFactor, 1.0f, m_zScaleFactor); QVector4D backgroundColor = Utils::vectorFromColor(m_cachedTheme->backgroundColor()); -#ifdef USE_REFLECTIONS - backgroundColor.setW(backgroundColor.w() * reflection); -#endif + if (m_reflectionEnabled) + backgroundColor.setW(backgroundColor.w() * m_reflectivity); // Set shader bindings m_backgroundShader->setUniformValue(m_backgroundShader->lightP(), lightPos); @@ -1748,9 +1718,7 @@ void Bars3DRenderer::drawBackground(GLfloat backgroundRotation, m_backgroundShader->setUniformValue(m_backgroundShader->nModel(), itModelMatrix.inverted().transposed()); m_backgroundShader->setUniformValue(m_backgroundShader->MVP(), MVPMatrix); -#ifdef USE_REFLECTIONS - if (reflection != 1.0f) { -#endif + if (!m_reflectionEnabled || (m_reflectionEnabled && reflectingDraw)) { #if !defined(QT_OPENGL_ES_2) if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { // Set shadow shader bindings @@ -1776,9 +1744,7 @@ void Bars3DRenderer::drawBackground(GLfloat backgroundRotation, // Draw the object m_drawer->drawObject(m_backgroundShader, m_backgroundObj); } -#ifdef USE_REFLECTIONS } -#endif } } @@ -2539,6 +2505,10 @@ void Bars3DRenderer::updateShadowQuality(QAbstract3DGraph::ShadowQuality quality // Re-init depth buffer updateDepthBuffer(); #endif + + // Redraw to handle both reflections and shadows on background + if (m_reflectionEnabled) + needRender(); } void Bars3DRenderer::loadBackgroundMesh() diff --git a/src/datavisualization/engine/bars3drenderer_p.h b/src/datavisualization/engine/bars3drenderer_p.h index cf29dc21..44837ba2 100644 --- a/src/datavisualization/engine/bars3drenderer_p.h +++ b/src/datavisualization/engine/bars3drenderer_p.h @@ -149,22 +149,13 @@ private: void drawLabels(bool drawSelection, const Q3DCamera *activeCamera, const QMatrix4x4 &viewMatrix, const QMatrix4x4 &projectionMatrix); -#ifdef USE_REFLECTIONS bool drawBars(BarRenderItem **selectedBar, const QMatrix4x4 &depthProjectionViewMatrix, const QMatrix4x4 &projectionViewMatrix, const QMatrix4x4 &viewMatrix, GLint startRow, GLint stopRow, GLint stepRow, GLint startBar, GLint stopBar, GLint stepBar, GLfloat reflection = 1.0f); void drawBackground(GLfloat backgroundRotation, const QMatrix4x4 &depthProjectionViewMatrix, const QMatrix4x4 &projectionViewMatrix, const QMatrix4x4 &viewMatrix, - GLfloat reflection = 1.0f); -#else - bool drawBars(BarRenderItem **selectedBar, const QMatrix4x4 &depthProjectionViewMatrix, - const QMatrix4x4 &projectionViewMatrix, const QMatrix4x4 &viewMatrix, - GLint startRow, GLint stopRow, GLint stepRow, - GLint startBar, GLint stopBar, GLint stepBar); - void drawBackground(GLfloat backgroundRotation, const QMatrix4x4 &depthProjectionViewMatrix, - const QMatrix4x4 &projectionViewMatrix, const QMatrix4x4 &viewMatrix); -#endif + bool reflectingDraw = false); void drawGridLines(const QMatrix4x4 &depthProjectionViewMatrix, const QMatrix4x4 &projectionViewMatrix, const QMatrix4x4 &viewMatrix); diff --git a/src/datavisualization/engine/qabstract3dgraph.cpp b/src/datavisualization/engine/qabstract3dgraph.cpp index 3f199dab..2b9fc92a 100644 --- a/src/datavisualization/engine/qabstract3dgraph.cpp +++ b/src/datavisualization/engine/qabstract3dgraph.cpp @@ -722,6 +722,47 @@ qreal QAbstract3DGraph::horizontalAspectRatio() const return d_ptr->m_visualController->horizontalAspectRatio(); } +/*! + * \property QAbstract3DGraph::reflection + * \since QtDataVisualization 1.2 + * + * Sets floor reflections on or off. Defaults to \c{false}. + * + * \note Affects only Q3DBars. + * + * \sa reflectivity + */ +void QAbstract3DGraph::setReflection(bool enable) +{ + d_ptr->m_visualController->setReflection(enable); +} + +bool QAbstract3DGraph::isReflection() const +{ + return d_ptr->m_visualController->reflection(); +} + +/*! + * \property QAbstract3DGraph::reflectivity + * \since QtDataVisualization 1.2 + * + * Adjusts floor reflectivity, larger number being more reflective. Valid range is \c{[0...1]}. + * Defaults to \c{0.5}. + * + * \note Affects only Q3DBars. + * + * \sa reflection + */ +void QAbstract3DGraph::setReflectivity(qreal reflectivity) +{ + d_ptr->m_visualController->setReflectivity(reflectivity); +} + +qreal QAbstract3DGraph::reflectivity() const +{ + return d_ptr->m_visualController->reflectivity(); +} + /*! * \internal */ @@ -878,6 +919,11 @@ void QAbstract3DGraphPrivate::setVisualController(Abstract3DController *controll &QAbstract3DGraph::radialLabelOffsetChanged); QObject::connect(m_visualController, &Abstract3DController::horizontalAspectRatioChanged, q_ptr, &QAbstract3DGraph::horizontalAspectRatioChanged); + + QObject::connect(m_visualController, &Abstract3DController::reflectionChanged, q_ptr, + &QAbstract3DGraph::reflectionChanged); + QObject::connect(m_visualController, &Abstract3DController::reflectivityChanged, q_ptr, + &QAbstract3DGraph::reflectivityChanged); } void QAbstract3DGraphPrivate::handleDevicePixelRatioChange() diff --git a/src/datavisualization/engine/qabstract3dgraph.h b/src/datavisualization/engine/qabstract3dgraph.h index ba099395..e19a3e8e 100644 --- a/src/datavisualization/engine/qabstract3dgraph.h +++ b/src/datavisualization/engine/qabstract3dgraph.h @@ -53,6 +53,8 @@ class QT_DATAVISUALIZATION_EXPORT QAbstract3DGraph : public QWindow, protected Q Q_PROPERTY(bool polar READ isPolar WRITE setPolar NOTIFY polarChanged) Q_PROPERTY(float radialLabelOffset READ radialLabelOffset WRITE setRadialLabelOffset NOTIFY radialLabelOffsetChanged) Q_PROPERTY(qreal horizontalAspectRatio READ horizontalAspectRatio WRITE setHorizontalAspectRatio NOTIFY horizontalAspectRatioChanged) + Q_PROPERTY(bool reflection READ isReflection WRITE setReflection NOTIFY reflectionChanged) + Q_PROPERTY(qreal reflectivity READ reflectivity WRITE setReflectivity NOTIFY reflectivityChanged) protected: explicit QAbstract3DGraph(QAbstract3DGraphPrivate *d, const QSurfaceFormat *format, @@ -162,6 +164,12 @@ public: void setHorizontalAspectRatio(qreal ratio); qreal horizontalAspectRatio() const; + void setReflection(bool enable); + bool isReflection() const; + + void setReflectivity(qreal reflectivity); + qreal reflectivity() const; + protected: bool event(QEvent *event); void resizeEvent(QResizeEvent *event); @@ -188,6 +196,8 @@ signals: void polarChanged(bool enabled); void radialLabelOffsetChanged(float offset); void horizontalAspectRatioChanged(qreal ratio); + void reflectionChanged(bool enabled); + void reflectivityChanged(qreal reflectivity); private: Q_DISABLE_COPY(QAbstract3DGraph) diff --git a/src/datavisualizationqml2/abstractdeclarative.cpp b/src/datavisualizationqml2/abstractdeclarative.cpp index 8983046b..b062f78c 100644 --- a/src/datavisualizationqml2/abstractdeclarative.cpp +++ b/src/datavisualizationqml2/abstractdeclarative.cpp @@ -760,6 +760,26 @@ qreal AbstractDeclarative::horizontalAspectRatio() const return m_controller->horizontalAspectRatio(); } +void AbstractDeclarative::setReflection(bool enable) +{ + m_controller->setReflection(enable); +} + +bool AbstractDeclarative::isReflection() const +{ + return m_controller->reflection(); +} + +void AbstractDeclarative::setReflectivity(qreal reflectivity) +{ + m_controller->setReflectivity(reflectivity); +} + +qreal AbstractDeclarative::reflectivity() const +{ + return m_controller->reflectivity(); +} + void AbstractDeclarative::windowDestroyed(QObject *obj) { // Remove destroyed window from window lists diff --git a/src/datavisualizationqml2/abstractdeclarative_p.h b/src/datavisualizationqml2/abstractdeclarative_p.h index 058cb44a..f4bc41b7 100644 --- a/src/datavisualizationqml2/abstractdeclarative_p.h +++ b/src/datavisualizationqml2/abstractdeclarative_p.h @@ -75,6 +75,8 @@ class AbstractDeclarative : public QQuickItem Q_PROPERTY(bool polar READ isPolar WRITE setPolar NOTIFY polarChanged REVISION 2) Q_PROPERTY(float radialLabelOffset READ radialLabelOffset WRITE setRadialLabelOffset NOTIFY radialLabelOffsetChanged REVISION 2) Q_PROPERTY(qreal horizontalAspectRatio READ horizontalAspectRatio WRITE setHorizontalAspectRatio NOTIFY horizontalAspectRatioChanged REVISION 2) + Q_PROPERTY(bool reflection READ isReflection WRITE setReflection NOTIFY reflectionChanged REVISION 2) + Q_PROPERTY(qreal reflectivity READ reflectivity WRITE setReflectivity NOTIFY reflectivityChanged REVISION 2) public: enum SelectionFlag { @@ -205,6 +207,12 @@ public: void setHorizontalAspectRatio(qreal ratio); qreal horizontalAspectRatio() const; + void setReflection(bool enable); + bool isReflection() const; + + void setReflectivity(qreal reflectivity); + qreal reflectivity() const; + public slots: virtual void handleAxisXChanged(QAbstract3DAxis *axis) = 0; virtual void handleAxisYChanged(QAbstract3DAxis *axis) = 0; @@ -245,6 +253,8 @@ signals: Q_REVISION(2) void polarChanged(bool enabled); Q_REVISION(2) void radialLabelOffsetChanged(float offset); Q_REVISION(2) void horizontalAspectRatioChanged(qreal ratio); + Q_REVISION(2) void reflectionChanged(bool enabled); + Q_REVISION(2) void reflectivityChanged(qreal reflectivity); private: QPointer m_controller; diff --git a/tests/barstest/chart.cpp b/tests/barstest/chart.cpp index 445c235e..f451e480 100644 --- a/tests/barstest/chart.cpp +++ b/tests/barstest/chart.cpp @@ -1712,3 +1712,14 @@ void GraphModifier::toggleMultiseriesScaling() { m_graph->setMultiSeriesUniform(!m_graph->isMultiSeriesUniform()); } + +void GraphModifier::setReflection(bool enabled) +{ + m_graph->setReflection(enabled); +} + +void GraphModifier::setReflectivity(int value) +{ + qreal reflectivity = (qreal)value / 100.0; + m_graph->setReflectivity(reflectivity); +} diff --git a/tests/barstest/chart.h b/tests/barstest/chart.h index 6740df73..91be6e04 100644 --- a/tests/barstest/chart.h +++ b/tests/barstest/chart.h @@ -97,6 +97,8 @@ public: void setInputHandlerRotationEnabled(int enabled); void setInputHandlerZoomEnabled(int enabled); void setInputHandlerSelectionEnabled(int enabled); + void setReflection(bool enabled); + void setReflectivity(int value); public slots: void flipViews(); diff --git a/tests/barstest/main.cpp b/tests/barstest/main.cpp index bd891cd3..0dc29eb1 100644 --- a/tests/barstest/main.cpp +++ b/tests/barstest/main.cpp @@ -253,6 +253,15 @@ int main(int argc, char **argv) ratioSlider->setValue(30); ratioSlider->setMaximum(100); + QCheckBox *reflectionCheckBox = new QCheckBox(widget); + reflectionCheckBox->setText(QStringLiteral("Show reflections")); + reflectionCheckBox->setChecked(false); + + QSlider *reflectivitySlider = new QSlider(Qt::Horizontal, widget); + reflectivitySlider->setMinimum(0); + reflectivitySlider->setValue(50); + reflectivitySlider->setMaximum(100); + QSlider *spacingSliderX = new QSlider(Qt::Horizontal, widget); spacingSliderX->setTickInterval(1); spacingSliderX->setMinimum(0); @@ -420,8 +429,9 @@ int main(int argc, char **argv) vLayout3->addWidget(new QLabel(QStringLiteral("Camera target")), 0, Qt::AlignTop); vLayout3->addWidget(cameraTargetSliderX, 0, Qt::AlignTop); vLayout3->addWidget(cameraTargetSliderY, 0, Qt::AlignTop); - vLayout3->addWidget(cameraTargetSliderZ, 1, Qt::AlignTop); - // TODO: Add example for setMeshFileName + vLayout3->addWidget(cameraTargetSliderZ, 0, Qt::AlignTop); + vLayout3->addWidget(reflectionCheckBox, 0, Qt::AlignTop); + vLayout3->addWidget(reflectivitySlider, 1, Qt::AlignTop); widget->show(); @@ -550,6 +560,11 @@ int main(int argc, char **argv) QObject::connect(rotationCheckBox, &QCheckBox::stateChanged, rotationSliderY, &QSlider::setValue); + QObject::connect(reflectionCheckBox, &QCheckBox::stateChanged, modifier, + &GraphModifier::setReflection); + QObject::connect(reflectivitySlider, &QSlider::valueChanged, modifier, + &GraphModifier::setReflectivity); + QObject::connect(staticCheckBox, &QCheckBox::stateChanged, addDataButton, &QPushButton::setEnabled); QObject::connect(staticCheckBox, &QCheckBox::stateChanged, addMultiDataButton, diff --git a/tests/qmlcamera/qml/qmlcamera/main.qml b/tests/qmlcamera/qml/qmlcamera/main.qml index cb1737f6..cb22af59 100644 --- a/tests/qmlcamera/qml/qmlcamera/main.qml +++ b/tests/qmlcamera/qml/qmlcamera/main.qml @@ -41,7 +41,7 @@ Rectangle { xRotation: camControlArea.xValue yRotation: camControlArea.yValue zoomLevel: zoomSlider.value - target: Qt.vector3d(1.0, 1.0, 1.0) + target: Qt.vector3d(0.5, 0.5, 0.5) } Item { @@ -214,4 +214,20 @@ Rectangle { } } } + + Button { + id: reflectionToggle + anchors.bottom: shuttleAdd.top + width: camControlArea.width + text: "Show reflections" + onClicked: { + if (testChart.reflection === true) { + text = "Show reflections" + testChart.reflection = false + } else { + text = "Hide reflections" + testChart.reflection = true + } + } + } } -- cgit v1.2.3 From c11538865bf8879cf2fb8aa4b0d057e1804eca37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomi=20Korpip=C3=A4=C3=A4?= Date: Tue, 26 Aug 2014 13:40:51 +0300 Subject: Added reflection to 2 examples MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Task-number: QTRD-3290 + changed the theme in texturesurface example Change-Id: I872ef7048e21c3bbf9daa0b2a620c9cdfe2256c8 Reviewed-by: Tomi Korpipää --- .../bars/doc/images/bars-example.png | Bin 188289 -> 238712 bytes examples/datavisualization/bars/graphmodifier.cpp | 5 +++++ examples/datavisualization/bars/graphmodifier.h | 1 + examples/datavisualization/bars/main.cpp | 8 ++++++++ .../customproxy/doc/images/customproxy-example.png | Bin 125698 -> 173744 bytes .../customproxy/rainfallgraph.cpp | 5 ++++- .../doc/images/texturesurface-example.png | Bin 71994 -> 157373 bytes .../texturesurface/surfacegraph.cpp | 2 +- 8 files changed, 19 insertions(+), 2 deletions(-) diff --git a/examples/datavisualization/bars/doc/images/bars-example.png b/examples/datavisualization/bars/doc/images/bars-example.png index fb79668d..c06fe2c1 100644 Binary files a/examples/datavisualization/bars/doc/images/bars-example.png and b/examples/datavisualization/bars/doc/images/bars-example.png differ diff --git a/examples/datavisualization/bars/graphmodifier.cpp b/examples/datavisualization/bars/graphmodifier.cpp index 656cee90..587bc1d6 100644 --- a/examples/datavisualization/bars/graphmodifier.cpp +++ b/examples/datavisualization/bars/graphmodifier.cpp @@ -420,3 +420,8 @@ void GraphModifier::setReverseValueAxis(int enabled) { m_graph->valueAxis()->setReversed(enabled); } + +void GraphModifier::setReflection(bool enabled) +{ + m_graph->setReflection(enabled); +} diff --git a/examples/datavisualization/bars/graphmodifier.h b/examples/datavisualization/bars/graphmodifier.h index 252af1b8..22b00923 100644 --- a/examples/datavisualization/bars/graphmodifier.h +++ b/examples/datavisualization/bars/graphmodifier.h @@ -50,6 +50,7 @@ public: void setSmoothBars(int smooth); void setSeriesVisibility(int enabled); void setReverseValueAxis(int enabled); + void setReflection(bool enabled); public slots: void changeRange(int range); diff --git a/examples/datavisualization/bars/main.cpp b/examples/datavisualization/bars/main.cpp index 0173f81c..a7df14e0 100644 --- a/examples/datavisualization/bars/main.cpp +++ b/examples/datavisualization/bars/main.cpp @@ -72,6 +72,7 @@ int main(int argc, char **argv) smoothCheckBox->setText(QStringLiteral("Smooth bars")); smoothCheckBox->setChecked(false); + QComboBox *barStyleList = new QComboBox(widget); barStyleList->addItem(QStringLiteral("Bar"), int(QAbstract3DSeries::MeshBar)); barStyleList->addItem(QStringLiteral("Pyramid"), int(QAbstract3DSeries::MeshPyramid)); @@ -139,6 +140,10 @@ int main(int argc, char **argv) reverseValueAxisCheckBox->setText(QStringLiteral("Reverse value axis")); reverseValueAxisCheckBox->setChecked(false); + QCheckBox *reflectionCheckBox = new QCheckBox(widget); + reflectionCheckBox->setText(QStringLiteral("Show reflections")); + reflectionCheckBox->setChecked(false); + //! [4] QSlider *rotationSliderX = new QSlider(Qt::Horizontal, widget); rotationSliderX->setTickInterval(30); @@ -213,6 +218,7 @@ int main(int argc, char **argv) vLayout->addWidget(backgroundCheckBox); vLayout->addWidget(gridCheckBox); vLayout->addWidget(smoothCheckBox); + vLayout->addWidget(reflectionCheckBox); vLayout->addWidget(seriesCheckBox); vLayout->addWidget(reverseValueAxisCheckBox); vLayout->addWidget(axisTitlesVisibleCB); @@ -260,6 +266,8 @@ int main(int argc, char **argv) &GraphModifier::setSeriesVisibility); QObject::connect(reverseValueAxisCheckBox, &QCheckBox::stateChanged, modifier, &GraphModifier::setReverseValueAxis); + QObject::connect(reflectionCheckBox, &QCheckBox::stateChanged, modifier, + &GraphModifier::setReflection); QObject::connect(modifier, &GraphModifier::backgroundEnabledChanged, backgroundCheckBox, &QCheckBox::setChecked); diff --git a/examples/datavisualization/customproxy/doc/images/customproxy-example.png b/examples/datavisualization/customproxy/doc/images/customproxy-example.png index 753b8951..4f82943a 100644 Binary files a/examples/datavisualization/customproxy/doc/images/customproxy-example.png and b/examples/datavisualization/customproxy/doc/images/customproxy-example.png differ diff --git a/examples/datavisualization/customproxy/rainfallgraph.cpp b/examples/datavisualization/customproxy/rainfallgraph.cpp index 024fc2a1..bcd67acc 100644 --- a/examples/datavisualization/customproxy/rainfallgraph.cpp +++ b/examples/datavisualization/customproxy/rainfallgraph.cpp @@ -49,7 +49,7 @@ RainfallGraph::RainfallGraph(Q3DBars *rainfall) // Set up bar specifications; make the bars as wide as they are deep, // and add a small space between the bars m_graph->setBarThickness(1.0f); - m_graph->setBarSpacing(QSizeF(0.2, 0.2)); + m_graph->setBarSpacing(QSizeF(1.1, 1.1)); // Set axis labels and titles QStringList months; @@ -85,6 +85,9 @@ RainfallGraph::RainfallGraph(Q3DBars *rainfall) // Set window title m_graph->setTitle(QStringLiteral("Monthly rainfall in Northern Finland")); + + // Set reflections on + m_graph->setReflection(true); } RainfallGraph::~RainfallGraph() diff --git a/examples/datavisualization/texturesurface/doc/images/texturesurface-example.png b/examples/datavisualization/texturesurface/doc/images/texturesurface-example.png index 384552b4..76819607 100644 Binary files a/examples/datavisualization/texturesurface/doc/images/texturesurface-example.png and b/examples/datavisualization/texturesurface/doc/images/texturesurface-example.png differ diff --git a/examples/datavisualization/texturesurface/surfacegraph.cpp b/examples/datavisualization/texturesurface/surfacegraph.cpp index 785cf576..e01a329d 100644 --- a/examples/datavisualization/texturesurface/surfacegraph.cpp +++ b/examples/datavisualization/texturesurface/surfacegraph.cpp @@ -43,7 +43,7 @@ SurfaceGraph::SurfaceGraph(Q3DSurface *surface) m_graph->axisX()->setLabelAutoRotation(30); m_graph->axisY()->setLabelAutoRotation(90); m_graph->axisZ()->setLabelAutoRotation(30); - m_graph->activeTheme()->setType(Q3DTheme::ThemeStoneMoss); + m_graph->activeTheme()->setType(Q3DTheme::ThemePrimaryColors); QFont font = m_graph->activeTheme()->font(); font.setPointSize(20); -- cgit v1.2.3 From 0d822bc5de5735b40fc0c76e366365ce1b6cab87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomi=20Korpip=C3=A4=C3=A4?= Date: Tue, 26 Aug 2014 13:59:20 +0300 Subject: Mac reflection bug fixed + some mingw & clang compilation fixes Change-Id: Ib4cd5534d9d4e630c021f3d9901114983cdc26eb Reviewed-by: Miikka Heikkinen --- src/datavisualization/engine/bars3drenderer.cpp | 10 ++++------ src/datavisualization/utils/qutils.h | 1 + 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/datavisualization/engine/bars3drenderer.cpp b/src/datavisualization/engine/bars3drenderer.cpp index 7e49c0b3..22bf6b87 100644 --- a/src/datavisualization/engine/bars3drenderer.cpp +++ b/src/datavisualization/engine/bars3drenderer.cpp @@ -908,8 +908,6 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) GLfloat colPos = 0; GLfloat rowPos = 0; - QVector4D lightColor = Utils::vectorFromColor(m_cachedTheme->lightColor()); - const Q3DCamera *activeCamera = m_cachedScene->activeCamera(); glViewport(m_primarySubViewport.x(), @@ -1043,8 +1041,8 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) shadowOffset = -0.015f; } - if (m_reflectionEnabled && (m_yFlipped && item.height() > 0.0 - || !m_yFlipped && item.height() < 0.0)) { + if (m_reflectionEnabled && ((m_yFlipped && item.height() > 0.0) + || (!m_yFlipped && item.height() < 0.0))) { continue; } @@ -1569,8 +1567,8 @@ bool Bars3DRenderer::drawBars(BarRenderItem **selectedBar, } else if ((m_reflectionEnabled && (reflection == 1.0f || (reflection != 1.0f - && (m_yFlipped && item.height() < 0.0) - || (!m_yFlipped && item.height() > 0.0)))) + && ((m_yFlipped && item.height() < 0.0) + || (!m_yFlipped && item.height() > 0.0))))) || !m_reflectionEnabled) { // Skip drawing of 0-height bars and reflections of bars on the "wrong side" // Set shader bindings diff --git a/src/datavisualization/utils/qutils.h b/src/datavisualization/utils/qutils.h index 43375a9c..b4ac17b4 100644 --- a/src/datavisualization/utils/qutils.h +++ b/src/datavisualization/utils/qutils.h @@ -28,6 +28,7 @@ inline static QSurfaceFormat qDefaultSurfaceFormat(bool antialias = true) QSurfaceFormat surfaceFormat; surfaceFormat.setDepthBufferSize(24); + surfaceFormat.setStencilBufferSize(8); surfaceFormat.setSwapBehavior(QSurfaceFormat::DoubleBuffer); #if !defined(QT_OPENGL_ES_2) surfaceFormat.setRenderableType(QSurfaceFormat::OpenGL); -- cgit v1.2.3 From 15a088a1b4d63df74b547a9b7d5d4d1fb311ec29 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Wed, 27 Aug 2014 09:30:26 +0300 Subject: Fix build issue with Qt 5.2.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit QImage is not a QObject, and therefore QImage::Format doesn't seem to be a supported type for a Q_PROPERTY, either. At least this was the case in Qt 5.2.1. In Qt 5.3.1 this worked. In any case, worked around the issue by removing textureFormat as a property. This shouldn't be an issue, since texture cannot meaningfully be manipulated from QML anyway. Also fixed some compile warnings. Change-Id: I257c1502d3340c49c24085fb8bf2e6176d875215 Reviewed-by: Tomi Korpipää --- src/datavisualization/data/qcustom3dvolume.cpp | 49 ++++++++++++++-------- src/datavisualization/data/qcustom3dvolume.h | 2 +- .../engine/abstract3drenderer.cpp | 2 +- src/datavisualization/engine/scatter3drenderer.cpp | 4 +- src/datavisualization/engine/surface3drenderer.cpp | 4 +- .../utils/abstractobjecthelper_p.h | 2 +- src/datavisualization/utils/objecthelper_p.h | 2 +- .../utils/scatterobjectbufferhelper.cpp | 2 +- .../utils/scatterobjectbufferhelper_p.h | 2 +- .../utils/scatterpointbufferhelper_p.h | 2 +- src/datavisualization/utils/surfaceobject_p.h | 2 +- 11 files changed, 44 insertions(+), 29 deletions(-) diff --git a/src/datavisualization/data/qcustom3dvolume.cpp b/src/datavisualization/data/qcustom3dvolume.cpp index c1a77dba..503d324d 100644 --- a/src/datavisualization/data/qcustom3dvolume.cpp +++ b/src/datavisualization/data/qcustom3dvolume.cpp @@ -148,7 +148,7 @@ QCustom3DVolume::QCustom3DVolume(QObject *parent) : * \a textureWidth, \a textureHeight, \a textureDepth, \a textureData, \a textureFormat, * \a colorTable, and optional \a parent. * - * \sa textureData, textureFormat, colorTable + * \sa textureData, setTextureFormat(), colorTable */ QCustom3DVolume::QCustom3DVolume(const QVector3D &position, const QVector3D &scaling, const QQuaternion &rotation, int textureWidth, @@ -176,7 +176,7 @@ QCustom3DVolume::~QCustom3DVolume() * \note The textureData may need to be resized or recreated if this value is changed. * Defaults to \c{0}. * - * \sa textureData, textureHeight, textureDepth, textureFormat, textureDataWidth() + * \sa textureData, textureHeight, textureDepth, setTextureFormat(), textureDataWidth() */ void QCustom3DVolume::setTextureWidth(int value) { @@ -204,7 +204,7 @@ int QCustom3DVolume::textureWidth() const * \note The textureData may need to be resized or recreated if this value is changed. * Defaults to \c{0}. * - * \sa textureData, textureWidth, textureDepth, textureFormat + * \sa textureData, textureWidth, textureDepth, setTextureFormat() */ void QCustom3DVolume::setTextureHeight(int value) { @@ -233,7 +233,7 @@ int QCustom3DVolume::textureHeight() const * \note The textureData may need to be resized or recreated if this value is changed. * Defaults to \c{0}. * - * \sa textureData, textureWidth, textureHeight, textureFormat + * \sa textureData, textureWidth, textureHeight, setTextureFormat() */ void QCustom3DVolume::setTextureDepth(int value) { @@ -374,7 +374,7 @@ void QCustom3DVolume::setSliceIndices(int x, int y, int z) * * Defaults to \c{0}. * - * \sa textureData, textureFormat, QImage::colorTable() + * \sa textureData, setTextureFormat(), QImage::colorTable() */ void QCustom3DVolume::setColorTable(const QVector &colors) { @@ -414,7 +414,7 @@ QVector QCustom3DVolume::colorTable() const * * Defaults to \c{0}. * - * \sa colorTable, textureFormat, setSubTextureData(), textureDataWidth() + * \sa colorTable, setTextureFormat(), setSubTextureData(), textureDataWidth() */ void QCustom3DVolume::setTextureData(QVector *data) { @@ -439,7 +439,7 @@ void QCustom3DVolume::setTextureData(QVector *data) * * \return pointer to the newly created array. * - * \sa textureData, textureWidth, textureHeight, textureDepth, textureFormat + * \sa textureData, textureWidth, textureHeight, textureDepth, setTextureFormat() */ QVector *QCustom3DVolume::createTextureData(const QVector &images) { @@ -573,9 +573,11 @@ void QCustom3DVolume::setSubTextureData(int depthIndex, const QImage &image) } } -/*! \property QCustom3DVolume::textureFormat - * - * The format of the textureData. Only two formats are supported currently: +// Note: textureFormat is not a Q_PROPERTY to work around an issue in meta object system that +// doesn't allow QImage::format to be a property type. Qt 5.2.1 at least has this problem. + +/*! + * Sets the format of the textureData to \a format. Only two formats are supported currently: * QImage::Format_Indexed8 and QImage::Format_ARGB32. If an indexed format is specified, colorTable * must also be set. * Defaults to QImage::Format_ARGB32. @@ -596,11 +598,24 @@ void QCustom3DVolume::setTextureFormat(QImage::Format format) } } +/*! + * \return the format of the textureData. + * + * \sa setTextureFormat() + */ QImage::Format QCustom3DVolume::textureFormat() const { return dptrc()->m_textureFormat; } +/*! + * \fn void QCustom3DVolume::textureFormatChanged(QImage::Format format) + * + * This signal is emitted when the textureData \a format changes. + * + * \sa setTextureFormat() + */ + /*! * \property QCustom3DVolume::alphaMultiplier * @@ -663,7 +678,7 @@ bool QCustom3DVolume::preserveOpacity() const * * \return the rendered image of the slice, or a null image if invalid index is specified. * - * \sa textureFormat + * \sa setTextureFormat() */ QImage QCustom3DVolume::renderSlice(Qt::Axis axis, int index) { @@ -688,12 +703,12 @@ const QCustom3DVolumePrivate *QCustom3DVolume::dptrc() const QCustom3DVolumePrivate::QCustom3DVolumePrivate(QCustom3DVolume *q) : QCustom3DItemPrivate(q), - m_sliceIndexX(-1), - m_sliceIndexY(-1), - m_sliceIndexZ(-1), m_textureWidth(0), m_textureHeight(0), m_textureDepth(0), + m_sliceIndexX(-1), + m_sliceIndexY(-1), + m_sliceIndexZ(-1), m_textureFormat(QImage::Format_ARGB32), m_textureData(0), m_alphaMultiplier(1.0f), @@ -711,12 +726,12 @@ QCustom3DVolumePrivate::QCustom3DVolumePrivate(QCustom3DVolume *q, const QVector QImage::Format textureFormat, const QVector &colorTable) : QCustom3DItemPrivate(q, QStringLiteral(":/defaultMeshes/barFull"), position, scaling, rotation), - m_sliceIndexX(-1), - m_sliceIndexY(-1), - m_sliceIndexZ(-1), m_textureWidth(textureWidth), m_textureHeight(textureHeight), m_textureDepth(textureDepth), + m_sliceIndexX(-1), + m_sliceIndexY(-1), + m_sliceIndexZ(-1), m_textureFormat(textureFormat), m_colorTable(colorTable), m_textureData(textureData), diff --git a/src/datavisualization/data/qcustom3dvolume.h b/src/datavisualization/data/qcustom3dvolume.h index 2f95fa5d..8627eb71 100644 --- a/src/datavisualization/data/qcustom3dvolume.h +++ b/src/datavisualization/data/qcustom3dvolume.h @@ -39,9 +39,9 @@ class QT_DATAVISUALIZATION_EXPORT QCustom3DVolume : public QCustom3DItem Q_PROPERTY(int sliceIndexZ READ sliceIndexZ WRITE setSliceIndexZ NOTIFY sliceIndexZChanged) Q_PROPERTY(QVector colorTable READ colorTable WRITE setColorTable NOTIFY colorTableChanged) Q_PROPERTY(QVector *textureData READ textureData WRITE setTextureData NOTIFY textureDataChanged) - Q_PROPERTY(QImage::Format textureFormat READ textureFormat WRITE setTextureFormat NOTIFY textureFormatChanged) Q_PROPERTY(float alphaMultiplier READ alphaMultiplier WRITE setAlphaMultiplier NOTIFY alphaMultiplierChanged) Q_PROPERTY(bool preserveOpacity READ preserveOpacity WRITE setPreserveOpacity NOTIFY preserveOpacityChanged) + public: explicit QCustom3DVolume(QObject *parent = 0); diff --git a/src/datavisualization/engine/abstract3drenderer.cpp b/src/datavisualization/engine/abstract3drenderer.cpp index 7cf7f020..9f0e2d8f 100644 --- a/src/datavisualization/engine/abstract3drenderer.cpp +++ b/src/datavisualization/engine/abstract3drenderer.cpp @@ -930,7 +930,7 @@ CustomRenderItem *Abstract3DRenderer::addCustomItem(QCustom3DItem *item) QVector3D scaling = item->scaling(); QImage textureImage = item->d_ptr->textureImage(); bool facingCamera = false; - GLuint texture; + GLuint texture = 0; if (item->d_ptr->m_isLabelItem) { QCustom3DLabel *labelItem = static_cast(item); float pointSize = labelItem->font().pointSizeF(); diff --git a/src/datavisualization/engine/scatter3drenderer.cpp b/src/datavisualization/engine/scatter3drenderer.cpp index bc3893b9..94d49e3c 100644 --- a/src/datavisualization/engine/scatter3drenderer.cpp +++ b/src/datavisualization/engine/scatter3drenderer.cpp @@ -1748,8 +1748,8 @@ void Scatter3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa QQuaternion totalRotation = Utils::calculateRotation(labelRotation); if (m_polarGraph) { - if (!m_yFlippedForGrid && (m_zFlipped != m_xFlipped) - || m_yFlippedForGrid && (m_zFlipped == m_xFlipped)) { + if ((!m_yFlippedForGrid && (m_zFlipped != m_xFlipped)) + || (m_yFlippedForGrid && (m_zFlipped == m_xFlipped))) { totalRotation *= m_zRightAngleRotation; } else { totalRotation *= m_zRightAngleRotationNeg; diff --git a/src/datavisualization/engine/surface3drenderer.cpp b/src/datavisualization/engine/surface3drenderer.cpp index 17be3278..58f97b0a 100644 --- a/src/datavisualization/engine/surface3drenderer.cpp +++ b/src/datavisualization/engine/surface3drenderer.cpp @@ -2159,8 +2159,8 @@ void Surface3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa QQuaternion totalRotation = Utils::calculateRotation(labelRotation); if (m_polarGraph) { - if (!m_yFlippedForGrid && (m_zFlipped != m_xFlipped) - || m_yFlippedForGrid && (m_zFlipped == m_xFlipped)) { + if ((!m_yFlippedForGrid && (m_zFlipped != m_xFlipped)) + || (m_yFlippedForGrid && (m_zFlipped == m_xFlipped))) { totalRotation *= m_zRightAngleRotation; } else { totalRotation *= m_zRightAngleRotationNeg; diff --git a/src/datavisualization/utils/abstractobjecthelper_p.h b/src/datavisualization/utils/abstractobjecthelper_p.h index b12bc474..99f85dab 100644 --- a/src/datavisualization/utils/abstractobjecthelper_p.h +++ b/src/datavisualization/utils/abstractobjecthelper_p.h @@ -38,7 +38,7 @@ class AbstractObjectHelper: protected QOpenGLFunctions protected: AbstractObjectHelper(); public: - ~AbstractObjectHelper(); + virtual ~AbstractObjectHelper(); GLuint vertexBuf(); GLuint normalBuf(); diff --git a/src/datavisualization/utils/objecthelper_p.h b/src/datavisualization/utils/objecthelper_p.h index c84f53bd..b00e5d8d 100644 --- a/src/datavisualization/utils/objecthelper_p.h +++ b/src/datavisualization/utils/objecthelper_p.h @@ -41,7 +41,7 @@ class ObjectHelper : public AbstractObjectHelper private: ObjectHelper(const QString &objectFile); public: - ~ObjectHelper(); + virtual ~ObjectHelper(); static void resetObjectHelper(const Abstract3DRenderer *cacheId, ObjectHelper *&obj, const QString &meshFile); diff --git a/src/datavisualization/utils/scatterobjectbufferhelper.cpp b/src/datavisualization/utils/scatterobjectbufferhelper.cpp index 0ac1dd87..1639eb1f 100644 --- a/src/datavisualization/utils/scatterobjectbufferhelper.cpp +++ b/src/datavisualization/utils/scatterobjectbufferhelper.cpp @@ -187,7 +187,7 @@ void ScatterObjectBufferHelper::updateUVs(ScatterSeriesRenderCache *cache) QVector buffered_uvs; buffered_uvs.resize(uvsCount * renderArraySize); - uint itemCount; + uint itemCount = 0; if (cache->colorStyle() == Q3DTheme::ColorStyleRangeGradient) { itemCount = createRangeGradientUVs(cache, buffered_uvs); } else if (cache->colorStyle() == Q3DTheme::ColorStyleObjectGradient) { diff --git a/src/datavisualization/utils/scatterobjectbufferhelper_p.h b/src/datavisualization/utils/scatterobjectbufferhelper_p.h index c45febd1..08a42900 100644 --- a/src/datavisualization/utils/scatterobjectbufferhelper_p.h +++ b/src/datavisualization/utils/scatterobjectbufferhelper_p.h @@ -39,7 +39,7 @@ class ScatterObjectBufferHelper : public AbstractObjectHelper { public: ScatterObjectBufferHelper(); - ~ScatterObjectBufferHelper(); + virtual ~ScatterObjectBufferHelper(); void fullLoad(ScatterSeriesRenderCache *cache, qreal dotScale); void update(ScatterSeriesRenderCache *cache, qreal dotScale); diff --git a/src/datavisualization/utils/scatterpointbufferhelper_p.h b/src/datavisualization/utils/scatterpointbufferhelper_p.h index b3adcfa8..d09ce0f7 100644 --- a/src/datavisualization/utils/scatterpointbufferhelper_p.h +++ b/src/datavisualization/utils/scatterpointbufferhelper_p.h @@ -39,7 +39,7 @@ class ScatterPointBufferHelper : public AbstractObjectHelper { public: ScatterPointBufferHelper(); - ~ScatterPointBufferHelper(); + virtual ~ScatterPointBufferHelper(); GLuint pointBuf(); diff --git a/src/datavisualization/utils/surfaceobject_p.h b/src/datavisualization/utils/surfaceobject_p.h index 3266c626..f373eb5d 100644 --- a/src/datavisualization/utils/surfaceobject_p.h +++ b/src/datavisualization/utils/surfaceobject_p.h @@ -51,7 +51,7 @@ public: public: SurfaceObject(Surface3DRenderer *renderer); - ~SurfaceObject(); + virtual ~SurfaceObject(); void setUpData(const QSurfaceDataArray &dataArray, const QRect &space, bool changeGeometry, bool polar, bool flipXZ = false); -- cgit v1.2.3 From 6ec2eb3147b02dab62ea8ac3cff0b706628dc353 Mon Sep 17 00:00:00 2001 From: Mika Salmela Date: Wed, 27 Aug 2014 11:36:41 +0300 Subject: Diffuse and specular color for static optimization Change-Id: I2c51ba06c6af9193a70f4a268dfd2fbd6c6910b3 Reviewed-by: Miikka Heikkinen --- .../engine/abstract3drenderer.cpp | 33 ++++++++++++++++------ src/datavisualization/engine/engine.qrc | 2 ++ src/datavisualization/engine/qabstract3dgraph.cpp | 12 ++++---- src/datavisualization/engine/scatter3drenderer.cpp | 17 +---------- .../utils/scatterobjectbufferhelper.cpp | 20 +++++++------ tests/directional/main.cpp | 7 +++++ tests/directional/scatterdatamodifier.cpp | 9 +++++- tests/directional/scatterdatamodifier.h | 1 + 8 files changed, 60 insertions(+), 41 deletions(-) diff --git a/src/datavisualization/engine/abstract3drenderer.cpp b/src/datavisualization/engine/abstract3drenderer.cpp index 9f0e2d8f..090a833a 100644 --- a/src/datavisualization/engine/abstract3drenderer.cpp +++ b/src/datavisualization/engine/abstract3drenderer.cpp @@ -25,6 +25,7 @@ #include "qcustom3ditem_p.h" #include "qcustom3dlabel_p.h" #include "qcustom3dvolume_p.h" +#include "scatter3drenderer_p.h" #include @@ -282,19 +283,35 @@ void Abstract3DRenderer::reInitShaders() { #if !defined(QT_OPENGL_ES_2) if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { - initGradientShaders(QStringLiteral(":/shaders/vertexShadow"), - QStringLiteral(":/shaders/fragmentShadowNoTexColorOnY")); - initShaders(QStringLiteral(":/shaders/vertexShadow"), - QStringLiteral(":/shaders/fragmentShadowNoTex")); + if (m_cachedOptimizationHint.testFlag(QAbstract3DGraph::OptimizationStatic) + && qobject_cast(this)) { + initGradientShaders(QStringLiteral(":/shaders/vertexShadow"), + QStringLiteral(":/shaders/fragmentShadow")); + initShaders(QStringLiteral(":/shaders/vertexShadowNoMatrices"), + QStringLiteral(":/shaders/fragmentShadowNoTex")); + } else { + initGradientShaders(QStringLiteral(":/shaders/vertexShadow"), + QStringLiteral(":/shaders/fragmentShadowNoTexColorOnY")); + initShaders(QStringLiteral(":/shaders/vertexShadow"), + QStringLiteral(":/shaders/fragmentShadowNoTex")); + } initBackgroundShaders(QStringLiteral(":/shaders/vertexShadow"), QStringLiteral(":/shaders/fragmentShadowNoTex")); initCustomItemShaders(QStringLiteral(":/shaders/vertexShadow"), QStringLiteral(":/shaders/fragmentShadow")); } else { - initGradientShaders(QStringLiteral(":/shaders/vertex"), - QStringLiteral(":/shaders/fragmentColorOnY")); - initShaders(QStringLiteral(":/shaders/vertex"), - QStringLiteral(":/shaders/fragment")); + if (m_cachedOptimizationHint.testFlag(QAbstract3DGraph::OptimizationStatic) + && qobject_cast(this)) { + initGradientShaders(QStringLiteral(":/shaders/vertexTexture"), + QStringLiteral(":/shaders/fragmentTexture")); + initShaders(QStringLiteral(":/shaders/vertexNoMatrices"), + QStringLiteral(":/shaders/fragment")); + } else { + initGradientShaders(QStringLiteral(":/shaders/vertex"), + QStringLiteral(":/shaders/fragmentColorOnY")); + initShaders(QStringLiteral(":/shaders/vertex"), + QStringLiteral(":/shaders/fragment")); + } initBackgroundShaders(QStringLiteral(":/shaders/vertex"), QStringLiteral(":/shaders/fragment")); initCustomItemShaders(QStringLiteral(":/shaders/vertexTexture"), diff --git a/src/datavisualization/engine/engine.qrc b/src/datavisualization/engine/engine.qrc index 936ed53f..88440bec 100644 --- a/src/datavisualization/engine/engine.qrc +++ b/src/datavisualization/engine/engine.qrc @@ -62,5 +62,7 @@ shaders/texture3d.frag shaders/texture3d.vert shaders/texture3dslice.frag + shaders/shadowNoMatrices.vert + shaders/defaultNoMatrices.vert diff --git a/src/datavisualization/engine/qabstract3dgraph.cpp b/src/datavisualization/engine/qabstract3dgraph.cpp index 2b9fc92a..c797f554 100644 --- a/src/datavisualization/engine/qabstract3dgraph.cpp +++ b/src/datavisualization/engine/qabstract3dgraph.cpp @@ -148,7 +148,7 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION \value OptimizationDefault Provides the full feature set at a reasonable performance. \value OptimizationStatic - Beta level feature. Optimizes the rendering of static data sets at the expense of some features. + Optimizes the rendering of static data sets at the expense of some features. */ /*! @@ -636,12 +636,10 @@ qreal QAbstract3DGraph::aspectRatio() const * \property QAbstract3DGraph::optimizationHints * * Defines if the rendering optimization is default or static. Default mode provides the full feature set at - * reasonable performance. Static is a beta level feature and currently supports only a subset of the - * features on the Scatter graph. Missing features are both gradient color styles - * for points, and diffuse and specular color on rotations. At this point static is intended just for - * introducing a new feature. It optimizes graph rendering and is ideal for large non-changing data - * sets. It is slower with dynamic data changes and item rotations. Selection is not optimized, so using it - * with massive data sets is not advisable. + * reasonable performance. Static optimizes graph rendering and is ideal for large non-changing data sets. + * It is slower with dynamic data changes and item rotations. Selection is not optimized, so using it with + * massive data sets is not advisable. Static works only on the Scatter graph and lacks the range gradient + * color style for points. * Defaults to \c{OptimizationDefault}. */ void QAbstract3DGraph::setOptimizationHints(OptimizationHints hints) diff --git a/src/datavisualization/engine/scatter3drenderer.cpp b/src/datavisualization/engine/scatter3drenderer.cpp index 94d49e3c..4b6a258e 100644 --- a/src/datavisualization/engine/scatter3drenderer.cpp +++ b/src/datavisualization/engine/scatter3drenderer.cpp @@ -341,22 +341,7 @@ void Scatter3DRenderer::updateOptimizationHint(QAbstract3DGraph::OptimizationHin { Abstract3DRenderer::updateOptimizationHint(hint); - if (m_cachedOptimizationHint.testFlag(QAbstract3DGraph::OptimizationStatic)) { -#if !defined(QT_OPENGL_ES_2) - if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { - initGradientShaders(QStringLiteral(":/shaders/vertexShadow"), - QStringLiteral(":/shaders/fragmentShadow")); - } else { - initGradientShaders(QStringLiteral(":/shaders/vertexTexture"), - QStringLiteral(":/shaders/fragmentTexture")); - } -#else - initGradientShaders(QStringLiteral(":/shaders/vertexTexture"), - QStringLiteral(":/shaders/fragmentTexture")); -#endif - } else { - Abstract3DRenderer::reInitShaders(); - } + Abstract3DRenderer::reInitShaders(); } void Scatter3DRenderer::resetClickedStatus() diff --git a/src/datavisualization/utils/scatterobjectbufferhelper.cpp b/src/datavisualization/utils/scatterobjectbufferhelper.cpp index 1639eb1f..9ecdffdd 100644 --- a/src/datavisualization/utils/scatterobjectbufferhelper.cpp +++ b/src/datavisualization/utils/scatterobjectbufferhelper.cpp @@ -114,23 +114,25 @@ void ScatterObjectBufferHelper::fullLoad(ScatterSeriesRenderCache *cache, qreal int offset = pos * verticeCount; if (item.rotation().isIdentity()) { - for (int j = 0; j < verticeCount; j++) + for (int j = 0; j < verticeCount; j++) { buffered_vertices[j + offset] = scaled_vertices[j] + item.translation(); + buffered_normals[j + offset] = indexed_normals[j]; + } } else { QMatrix4x4 matrix; - matrix.rotate(seriesRotation * item.rotation()); - modelMatrix = matrix.transposed(); - modelMatrix.scale(modelScaler); + QQuaternion totalRotation = seriesRotation * item.rotation(); + matrix.rotate(totalRotation); + matrix.scale(modelScaler); + QMatrix4x4 itModelMatrix = matrix.inverted(); + modelMatrix = matrix.transposed(); // Because of row-column major difference - for (int j = 0; j < verticeCount; j++) + for (int j = 0; j < verticeCount; j++) { buffered_vertices[j + offset] = indexed_vertices[j] * modelMatrix + item.translation(); + buffered_normals[j + offset] = indexed_normals[j] * itModelMatrix; + } } - offset = pos * normalsCount; - for (int j = 0; j < normalsCount; j++) - buffered_normals[j + offset] = indexed_normals[j]; - if (cache->colorStyle() == Q3DTheme::ColorStyleUniform) { offset = pos * uvsCount; for (int j = 0; j < uvsCount; j++) diff --git a/tests/directional/main.cpp b/tests/directional/main.cpp index 2b077b97..551ab141 100644 --- a/tests/directional/main.cpp +++ b/tests/directional/main.cpp @@ -80,6 +80,9 @@ int main(int argc, char **argv) backgroundCheckBox->setText(QStringLiteral("Show background")); backgroundCheckBox->setChecked(true); + QCheckBox *optimizationCheckBox = new QCheckBox(widget); + optimizationCheckBox->setText(QStringLiteral("Optimization static")); + QCheckBox *gridCheckBox = new QCheckBox(widget); gridCheckBox->setText(QStringLiteral("Show grid")); gridCheckBox->setChecked(true); @@ -100,6 +103,7 @@ int main(int argc, char **argv) vLayout->addWidget(labelButton, 0, Qt::AlignTop); vLayout->addWidget(cameraButton, 0, Qt::AlignTop); vLayout->addWidget(toggleRotationButton, 0, Qt::AlignTop); + vLayout->addWidget(optimizationCheckBox); vLayout->addWidget(backgroundCheckBox); vLayout->addWidget(gridCheckBox); vLayout->addWidget(new QLabel(QStringLiteral("Change dot style"))); @@ -127,6 +131,8 @@ int main(int argc, char **argv) QObject::connect(modifier, &ScatterDataModifier::backgroundEnabledChanged, backgroundCheckBox, &QCheckBox::setChecked); + QObject::connect(optimizationCheckBox, &QCheckBox::stateChanged, + modifier, &ScatterDataModifier::enableOptimization); QObject::connect(modifier, &ScatterDataModifier::gridEnabledChanged, gridCheckBox, &QCheckBox::setChecked); QObject::connect(itemStyleList, SIGNAL(currentIndexChanged(int)), modifier, @@ -150,6 +156,7 @@ int main(int argc, char **argv) &QFontComboBox::setCurrentFont); itemStyleList->setCurrentIndex(0); + optimizationCheckBox->setChecked(true); widget->show(); return app.exec(); diff --git a/tests/directional/scatterdatamodifier.cpp b/tests/directional/scatterdatamodifier.cpp index 1422cebb..2d6672b6 100644 --- a/tests/directional/scatterdatamodifier.cpp +++ b/tests/directional/scatterdatamodifier.cpp @@ -119,7 +119,14 @@ void ScatterDataModifier::addData() m_graph->seriesList().at(0)->dataProxy()->resetArray(dataArray); } -//! [8] +void ScatterDataModifier::enableOptimization(int enabled) +{ + if (enabled) + m_graph->setOptimizationHints(QAbstract3DGraph::OptimizationStatic); + else + m_graph->setOptimizationHints(QAbstract3DGraph::OptimizationDefault); +} + void ScatterDataModifier::changeStyle(int style) { QComboBox *comboBox = qobject_cast(sender()); diff --git a/tests/directional/scatterdatamodifier.h b/tests/directional/scatterdatamodifier.h index b87fa89f..20e59cdc 100644 --- a/tests/directional/scatterdatamodifier.h +++ b/tests/directional/scatterdatamodifier.h @@ -39,6 +39,7 @@ public: void changeLabelStyle(); void changeFont(const QFont &font); void changeFontSize(int fontsize); + void enableOptimization(int enabled); void setBackgroundEnabled(int enabled); void setGridEnabled(int enabled); void toggleRotation(); -- cgit v1.2.3 From b2a039ca1db7cbf3e7b5950b277ac28b722df400 Mon Sep 17 00:00:00 2001 From: Mika Salmela Date: Wed, 27 Aug 2014 15:18:38 +0300 Subject: New vertex shaders for previous task. Change-Id: I992da6ca5835ac41b45410d79750e9f33f551793 Reviewed-by: Mika Salmela --- .../engine/shaders/defaultNoMatrices.vert | 26 ++++++++++++++++ .../engine/shaders/shadowNoMatrices.vert | 36 ++++++++++++++++++++++ 2 files changed, 62 insertions(+) create mode 100644 src/datavisualization/engine/shaders/defaultNoMatrices.vert create mode 100644 src/datavisualization/engine/shaders/shadowNoMatrices.vert diff --git a/src/datavisualization/engine/shaders/defaultNoMatrices.vert b/src/datavisualization/engine/shaders/defaultNoMatrices.vert new file mode 100644 index 00000000..cef10c19 --- /dev/null +++ b/src/datavisualization/engine/shaders/defaultNoMatrices.vert @@ -0,0 +1,26 @@ +attribute highp vec3 vertexPosition_mdl; +attribute highp vec2 vertexUV; +attribute highp vec3 vertexNormal_mdl; + +uniform highp mat4 MVP; +uniform highp mat4 V; +uniform highp vec3 lightPosition_wrld; + +varying highp vec3 lightPosition_wrld_frag; +varying highp vec3 position_wrld; +varying highp vec3 normal_cmr; +varying highp vec3 eyeDirection_cmr; +varying highp vec3 lightDirection_cmr; +varying highp vec2 coords_mdl; + +void main() { + gl_Position = MVP * vec4(vertexPosition_mdl, 1.0); + coords_mdl = vertexPosition_mdl.xy; + position_wrld = vertexPosition_mdl; + vec3 vertexPosition_cmr = vec4(V * vec4(vertexPosition_mdl, 1.0)).xyz; + eyeDirection_cmr = vec3(0.0, 0.0, 0.0) - vertexPosition_cmr; + vec3 lightPosition_cmr = vec4(V * vec4(lightPosition_wrld, 1.0)).xyz; + lightDirection_cmr = lightPosition_cmr + eyeDirection_cmr; + normal_cmr = vec4(V * vec4(vertexNormal_mdl, 0.0)).xyz; + lightPosition_wrld_frag = lightPosition_wrld; +} diff --git a/src/datavisualization/engine/shaders/shadowNoMatrices.vert b/src/datavisualization/engine/shaders/shadowNoMatrices.vert new file mode 100644 index 00000000..31748503 --- /dev/null +++ b/src/datavisualization/engine/shaders/shadowNoMatrices.vert @@ -0,0 +1,36 @@ +#version 120 + +uniform highp mat4 MVP; +uniform highp mat4 V; +uniform highp mat4 M; +uniform highp mat4 depthMVP; +uniform highp vec3 lightPosition_wrld; + +attribute highp vec3 vertexPosition_mdl; +attribute highp vec3 vertexNormal_mdl; +attribute highp vec2 vertexUV; + +varying highp vec2 UV; +varying highp vec3 position_wrld; +varying highp vec3 normal_cmr; +varying highp vec3 eyeDirection_cmr; +varying highp vec3 lightDirection_cmr; +varying highp vec4 shadowCoord; +varying highp vec2 coords_mdl; + +const highp mat4 bias = mat4(0.5, 0.0, 0.0, 0.0, + 0.0, 0.5, 0.0, 0.0, + 0.0, 0.0, 0.5, 0.0, + 0.5, 0.5, 0.5, 1.0); + +void main() { + gl_Position = MVP * vec4(vertexPosition_mdl, 1.0); + coords_mdl = vertexPosition_mdl.xy; + shadowCoord = bias * depthMVP * vec4(vertexPosition_mdl, 1.0); + position_wrld = vertexPosition_mdl; + vec3 vertexPosition_cmr = vec4(V * vec4(vertexPosition_mdl, 1.0)).xyz; + eyeDirection_cmr = vec3(0.0, 0.0, 0.0) - vertexPosition_cmr; + lightDirection_cmr = vec4(V * vec4(lightPosition_wrld, 0.0)).xyz; + normal_cmr = vec4(V * vec4(vertexNormal_mdl, 0.0)).xyz; + UV = vertexUV; +} -- cgit v1.2.3 From 9a40207d83b66072fff4aaa03eca15b02f8a11c1 Mon Sep 17 00:00:00 2001 From: Mika Salmela Date: Thu, 28 Aug 2014 10:29:39 +0300 Subject: Range gradient for points on static optimization Change-Id: Ic44a6a28617f272540d146f02bf50b4bd84cd9c8 Reviewed-by: Miikka Heikkinen --- ...tdatavisualization-qml-abstractdeclarative.qdoc | 11 ++--- src/datavisualization/engine/drawer.cpp | 22 +++++++++- src/datavisualization/engine/drawer_p.h | 2 +- src/datavisualization/engine/qabstract3dgraph.cpp | 3 +- src/datavisualization/engine/scatter3drenderer.cpp | 24 ++++++++--- .../utils/scatterobjectbufferhelper.cpp | 2 +- .../utils/scatterpointbufferhelper.cpp | 50 ++++++++++++++++++++++ .../utils/scatterpointbufferhelper_p.h | 7 +++ 8 files changed, 103 insertions(+), 18 deletions(-) diff --git a/src/datavisualization/doc/src/qtdatavisualization-qml-abstractdeclarative.qdoc b/src/datavisualization/doc/src/qtdatavisualization-qml-abstractdeclarative.qdoc index dfa65877..5dc7b35c 100644 --- a/src/datavisualization/doc/src/qtdatavisualization-qml-abstractdeclarative.qdoc +++ b/src/datavisualization/doc/src/qtdatavisualization-qml-abstractdeclarative.qdoc @@ -343,13 +343,10 @@ * \since QtDataVisualization 1.1 * * Defines if the rendering optimization is default or static. Default mode provides the full feature set at - * reasonable performance. Static is a beta level feature and currently supports only a subset of the - * features on the Scatter graph. Missing features are object gradient for mesh objects, both gradients - * for points, and diffuse and specular color on rotations. At this point static is intended just for - * introducing a new feature. It optimizes graph rendering and is ideal for large non-changing data - * sets. It is slower with dynamic data changes and item rotations. Selection is not optimized, so using it - * with massive data sets is not advisable. - * Defaults to \c{OptimizationDefault} + * reasonable performance. Static optimizes graph rendering and is ideal for large non-changing data sets. + * It is slower with dynamic data changes and item rotations. Selection is not optimized, so using it with + * massive data sets is not advisable. Static works only on the Scatter graph. + * Defaults to \c{OptimizationDefault}. */ /*! diff --git a/src/datavisualization/engine/drawer.cpp b/src/datavisualization/engine/drawer.cpp index 4191efc4..2cb88e5c 100644 --- a/src/datavisualization/engine/drawer.cpp +++ b/src/datavisualization/engine/drawer.cpp @@ -223,13 +223,27 @@ void Drawer::drawPoint(ShaderHelper *shader) glDisableVertexAttribArray(shader->posAtt()); } -void Drawer::drawPoints(ShaderHelper *shader, ScatterPointBufferHelper *object) +void Drawer::drawPoints(ShaderHelper *shader, ScatterPointBufferHelper *object, GLuint textureId) { + if (textureId) { + // Activate texture + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, textureId); + shader->setUniformValue(shader->texture(), 0); + } + // 1st attribute buffer : vertices glEnableVertexAttribArray(shader->posAtt()); glBindBuffer(GL_ARRAY_BUFFER, object->pointBuf()); glVertexAttribPointer(shader->posAtt(), 3, GL_FLOAT, GL_FALSE, 0, (void*)0); + // 2nd attribute buffer : UVs + if (textureId) { + glEnableVertexAttribArray(shader->uvAtt()); + glBindBuffer(GL_ARRAY_BUFFER, object->uvBuf()); + glVertexAttribPointer(shader->uvAtt(), 2, GL_FLOAT, GL_FALSE, 0, (void*)0); + } + // Draw the points glDrawArrays(GL_POINTS, 0, object->indexCount()); @@ -237,6 +251,12 @@ void Drawer::drawPoints(ShaderHelper *shader, ScatterPointBufferHelper *object) glBindBuffer(GL_ARRAY_BUFFER, 0); glDisableVertexAttribArray(shader->posAtt()); + + if (textureId) { + glDisableVertexAttribArray(shader->uvAtt()); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, 0); + } } void Drawer::drawLine(ShaderHelper *shader) diff --git a/src/datavisualization/engine/drawer_p.h b/src/datavisualization/engine/drawer_p.h index 709503dd..0c96724c 100644 --- a/src/datavisualization/engine/drawer_p.h +++ b/src/datavisualization/engine/drawer_p.h @@ -79,7 +79,7 @@ public: void drawSelectionObject(ShaderHelper *shader, AbstractObjectHelper *object); void drawSurfaceGrid(ShaderHelper *shader, SurfaceObject *object); void drawPoint(ShaderHelper *shader); - void drawPoints(ShaderHelper *shader, ScatterPointBufferHelper *object); + void drawPoints(ShaderHelper *shader, ScatterPointBufferHelper *object, GLuint textureId); void drawLine(ShaderHelper *shader); void drawLabel(const AbstractRenderItem &item, const LabelItem &labelItem, const QMatrix4x4 &viewmatrix, const QMatrix4x4 &projectionmatrix, diff --git a/src/datavisualization/engine/qabstract3dgraph.cpp b/src/datavisualization/engine/qabstract3dgraph.cpp index c797f554..03eeedf6 100644 --- a/src/datavisualization/engine/qabstract3dgraph.cpp +++ b/src/datavisualization/engine/qabstract3dgraph.cpp @@ -638,8 +638,7 @@ qreal QAbstract3DGraph::aspectRatio() const * Defines if the rendering optimization is default or static. Default mode provides the full feature set at * reasonable performance. Static optimizes graph rendering and is ideal for large non-changing data sets. * It is slower with dynamic data changes and item rotations. Selection is not optimized, so using it with - * massive data sets is not advisable. Static works only on the Scatter graph and lacks the range gradient - * color style for points. + * massive data sets is not advisable. Static works only on the Scatter graph. * Defaults to \c{OptimizationDefault}. */ void QAbstract3DGraph::setOptimizationHints(OptimizationHints hints) diff --git a/src/datavisualization/engine/scatter3drenderer.cpp b/src/datavisualization/engine/scatter3drenderer.cpp index 4b6a258e..0a670fe8 100644 --- a/src/datavisualization/engine/scatter3drenderer.cpp +++ b/src/datavisualization/engine/scatter3drenderer.cpp @@ -181,6 +181,7 @@ void Scatter3DRenderer::updateData() points = new ScatterPointBufferHelper(); cache->setBufferPoints(points); } + points->setScaleY(m_scaleY); points->load(cache); } else { ScatterObjectBufferHelper *object = cache->bufferObject(); @@ -219,7 +220,7 @@ void Scatter3DRenderer::updateSeries(const QList &seriesLis if (changeTracker.baseGradientChanged || changeTracker.colorStyleChanged) { ScatterSeriesRenderCache *cache = static_cast(m_renderCacheList.value(scatterSeries)); - if (cache && cache->mesh() != QAbstract3DSeries::MeshPoint) + if (cache) cache->setStaticObjectUVDirty(true); } } @@ -265,8 +266,13 @@ void Scatter3DRenderer::updateSeries(const QList &seriesLis } if (cache->staticObjectUVDirty()) { - ScatterObjectBufferHelper *object = cache->bufferObject(); - object->updateUVs(cache); + if (cache->mesh() == QAbstract3DSeries::MeshPoint) { + ScatterPointBufferHelper *object = cache->bufferPoints(); + object->updateUVs(cache); + } else { + ScatterObjectBufferHelper *object = cache->bufferObject(); + object->updateUVs(cache); + } cache->setStaticObjectUVDirty(false); } } @@ -522,7 +528,7 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) if (optimizationDefault) m_drawer->drawPoint(m_depthShader); else - m_drawer->drawPoints(m_depthShader, cache->bufferPoints()); + m_drawer->drawPoints(m_depthShader, cache->bufferPoints(), 0); } else { if (optimizationDefault) { // 1st attribute buffer : vertices @@ -853,6 +859,12 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) gradientTexture = cache->baseGradientTexture(); } + if (!optimizationDefault && rangeGradientPoints) { + dotShader = m_labelShader; + dotShader->bind(); + gradientTexture = cache->baseGradientTexture(); + } + GLfloat lightStrength = m_cachedTheme->lightStrength(); if (optimizationDefault && selectedSeries && (m_selectedItemIndex == i)) { if (useColor) @@ -905,7 +917,7 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) if (optimizationDefault) m_drawer->drawPoint(dotShader); else - m_drawer->drawPoints(dotShader, cache->bufferPoints()); + m_drawer->drawPoints(dotShader, cache->bufferPoints(), gradientTexture); } } else #endif @@ -923,7 +935,7 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) if (optimizationDefault) m_drawer->drawPoint(dotShader); else - m_drawer->drawPoints(dotShader, cache->bufferPoints()); + m_drawer->drawPoints(dotShader, cache->bufferPoints(), gradientTexture); } } } diff --git a/src/datavisualization/utils/scatterobjectbufferhelper.cpp b/src/datavisualization/utils/scatterobjectbufferhelper.cpp index 9ecdffdd..5e98363e 100644 --- a/src/datavisualization/utils/scatterobjectbufferhelper.cpp +++ b/src/datavisualization/utils/scatterobjectbufferhelper.cpp @@ -220,7 +220,7 @@ uint ScatterObjectBufferHelper::createRangeGradientUVs(ScatterSeriesRenderCache if (!item.isVisible()) continue; - float y = ((item.translation().y() + m_scaleY) * 0.5f ) / m_scaleY; + float y = ((item.translation().y() + m_scaleY) * 0.5f) / m_scaleY; uv.setY(y); int offset = pos * uvsCount; for (int j = 0; j < uvsCount; j++) diff --git a/src/datavisualization/utils/scatterpointbufferhelper.cpp b/src/datavisualization/utils/scatterpointbufferhelper.cpp index b14d84ad..2ddee31c 100644 --- a/src/datavisualization/utils/scatterpointbufferhelper.cpp +++ b/src/datavisualization/utils/scatterpointbufferhelper.cpp @@ -17,6 +17,7 @@ ****************************************************************************/ #include "scatterpointbufferhelper_p.h" +#include QT_BEGIN_NAMESPACE_DATAVISUALIZATION @@ -86,9 +87,14 @@ void ScatterPointBufferHelper::load(ScatterSeriesRenderCache *cache) if (m_meshDataLoaded) { // Delete old data glDeleteBuffers(1, &m_pointbuffer); + glDeleteBuffers(1, &m_uvbuffer); m_bufferedPoints.clear(); } + QVector buffered_uvs; + if (cache->colorStyle() == Q3DTheme::ColorStyleRangeGradient) + createRangeGradientUVs(cache, buffered_uvs); + bool itemsVisible = false; m_bufferedPoints.resize(renderArraySize); for (int i = 0; i < renderArraySize; i++) { @@ -110,10 +116,54 @@ void ScatterPointBufferHelper::load(ScatterSeriesRenderCache *cache) glBufferData(GL_ARRAY_BUFFER, m_bufferedPoints.size() * sizeof(QVector3D), &m_bufferedPoints.at(0), GL_DYNAMIC_DRAW); + + if (buffered_uvs.size()) { + glGenBuffers(1, &m_uvbuffer); + glBindBuffer(GL_ARRAY_BUFFER, m_uvbuffer); + glBufferData(GL_ARRAY_BUFFER, buffered_uvs.size() * sizeof(QVector2D), + &buffered_uvs.at(0), GL_STATIC_DRAW); + } + glBindBuffer(GL_ARRAY_BUFFER, 0); m_meshDataLoaded = true; } } +void ScatterPointBufferHelper::updateUVs(ScatterSeriesRenderCache *cache) +{ + QVector buffered_uvs; + + createRangeGradientUVs(cache, buffered_uvs); + + if (buffered_uvs.size()) { + if (!m_uvbuffer) + glGenBuffers(1, &m_uvbuffer); + + glBindBuffer(GL_ARRAY_BUFFER, m_uvbuffer); + glBufferData(GL_ARRAY_BUFFER, buffered_uvs.size() * sizeof(QVector2D), + &buffered_uvs.at(0), GL_STATIC_DRAW); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + } +} + +void ScatterPointBufferHelper::createRangeGradientUVs(ScatterSeriesRenderCache *cache, + QVector &buffered_uvs) +{ + const ScatterRenderItemArray &renderArray = cache->renderArray(); + const uint renderArraySize = renderArray.size(); + buffered_uvs.resize(renderArraySize); + + QVector2D uv; + uv.setX(0.0f); + for (uint i = 0; i < renderArraySize; i++) { + const ScatterRenderItem &item = renderArray.at(i); + + float y = ((item.translation().y() + m_scaleY) * 0.5f) / m_scaleY; + uv.setY(y); + buffered_uvs[i] = uv; + } +} + QT_END_NAMESPACE_DATAVISUALIZATION diff --git a/src/datavisualization/utils/scatterpointbufferhelper_p.h b/src/datavisualization/utils/scatterpointbufferhelper_p.h index d09ce0f7..3edc4c8f 100644 --- a/src/datavisualization/utils/scatterpointbufferhelper_p.h +++ b/src/datavisualization/utils/scatterpointbufferhelper_p.h @@ -46,14 +46,21 @@ public: void pushPoint(uint pointIndex); void popPoint(); void load(ScatterSeriesRenderCache *cache); + void setScaleY(float scale) { m_scaleY = scale; } + void updateUVs(ScatterSeriesRenderCache *cache); public: GLuint m_pointbuffer; +private: + void createRangeGradientUVs(ScatterSeriesRenderCache *cache, + QVector &buffered_uvs); + private: QVector m_bufferedPoints; uint m_oldRemoveIndex; bool m_oldRemove; + float m_scaleY; }; QT_END_NAMESPACE_DATAVISUALIZATION -- cgit v1.2.3 From cd1a66a8fcdcc6870656a924bf55ffbcd6ea1162 Mon Sep 17 00:00:00 2001 From: Mika Salmela Date: Fri, 29 Aug 2014 10:15:18 +0300 Subject: Unified gradient for points MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Made gradient image same size as the texture and similar calculation for the pixel. Result should be more coherent for all. Task-number: QTRD-3295 Change-Id: I666dacc6f525258ccefda288e26cac480cb385cb Reviewed-by: Miikka Heikkinen Reviewed-by: Tomi Korpipää --- src/datavisualization/engine/scatter3drenderer.cpp | 7 ++++--- src/datavisualization/utils/utils.cpp | 9 ++++++--- src/datavisualization/utils/utils_p.h | 2 +- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/datavisualization/engine/scatter3drenderer.cpp b/src/datavisualization/engine/scatter3drenderer.cpp index 0a670fe8..ca983dac 100644 --- a/src/datavisualization/engine/scatter3drenderer.cpp +++ b/src/datavisualization/engine/scatter3drenderer.cpp @@ -745,7 +745,6 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) } float rangeGradientYScaler = 0.5f / m_scaleY; - float rangeGradientYScalerForPoints = rangeGradientYScaler * 100.0f; foreach (SeriesRenderCache *baseCache, m_renderCacheList) { if (baseCache->isVisible()) { @@ -771,6 +770,8 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) glPointSize(itemSize * activeCamera->zoomLevel()); // Scale points based on zoom #endif QVector3D modelScaler(itemSize, itemSize, itemSize); + int gradientImageHeight = cache->gradientImage().height(); + int maxGradientPositition = gradientImageHeight - 1; if (!optimizationDefault && ((drawingPoints && cache->bufferPoints()->indexCount() == 0) @@ -848,8 +849,8 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) if (rangeGradientPoints) { // Drawing points with range gradient // Get color from gradient based on items y position converted to percent - int position = int(item.translation().y() * rangeGradientYScalerForPoints) - + 50; + int position = ((item.translation().y() + m_scaleY) * rangeGradientYScaler) * gradientImageHeight; + position = qMin(maxGradientPositition, position); // clamp to edge dotColor = Utils::vectorFromColor( cache->gradientImage().pixel(0, position)); } else { diff --git a/src/datavisualization/utils/utils.cpp b/src/datavisualization/utils/utils.cpp index 2368d86a..d7b8dae8 100644 --- a/src/datavisualization/utils/utils.cpp +++ b/src/datavisualization/utils/utils.cpp @@ -139,13 +139,16 @@ QVector4D Utils::getSelection(QPoint mousepos, int height) return selectedColor; } -QImage Utils::getGradientImage(const QLinearGradient &gradient) +QImage Utils::getGradientImage(QLinearGradient &gradient) { - QImage image(QSize(1, 101), QImage::Format_RGB32); + QImage image(QSize(gradientTextureWidth, gradientTextureHeight), QImage::Format_RGB32); + gradient.setFinalStop(qreal(gradientTextureWidth), qreal(gradientTextureHeight)); + gradient.setStart(0.0, 0.0); + QPainter pmp(&image); pmp.setBrush(QBrush(gradient)); pmp.setPen(Qt::NoPen); - pmp.drawRect(0, 0, 1, 101); + pmp.drawRect(0, 0, int(gradientTextureWidth), int(gradientTextureHeight)); return image; } diff --git a/src/datavisualization/utils/utils_p.h b/src/datavisualization/utils/utils_p.h index d7187c16..55707fdb 100644 --- a/src/datavisualization/utils/utils_p.h +++ b/src/datavisualization/utils/utils_p.h @@ -57,7 +57,7 @@ public: bool borders = false, int maxLabelWidth = 0); static QVector4D getSelection(QPoint mousepos, int height); - static QImage getGradientImage(const QLinearGradient &gradient); + static QImage getGradientImage(QLinearGradient &gradient); static ParamType findFormatParamType(const QString &format); static QString formatLabel(const QByteArray &format, ParamType paramType, qreal value); -- cgit v1.2.3 From a20806dac74415f3d8cb6679c9eae86ce074ddae Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Thu, 28 Aug 2014 15:42:52 +0300 Subject: Pixel perfect volume shader MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now the volume should render with pixel perfect accuracy. The drawback is that it is significantly slower than the old approximate solution. Change-Id: I4df7e9a28a27b56150c71a85a4c1fb69a215dabc Reviewed-by: Tomi Korpipää --- .../engine/abstract3drenderer.cpp | 12 +- .../engine/shaders/texture3d.frag | 162 ++++++++++++++------- .../engine/shaders/texture3dslice.frag | 29 ++-- 3 files changed, 128 insertions(+), 75 deletions(-) diff --git a/src/datavisualization/engine/abstract3drenderer.cpp b/src/datavisualization/engine/abstract3drenderer.cpp index 090a833a..78f4b600 100644 --- a/src/datavisualization/engine/abstract3drenderer.cpp +++ b/src/datavisualization/engine/abstract3drenderer.cpp @@ -1320,15 +1320,15 @@ void Abstract3DRenderer::drawCustomItems(RenderingState state, } else { // Precalculate texture dimensions so we can optimize // ray stepping to hit every texture layer. - QVector3D textureDimensions(float(item->textureWidth()), - float(item->textureHeight()), - float(item->textureDepth())); + QVector3D textureDimensions(1.0f / float(item->textureWidth()), + 1.0f / float(item->textureHeight()), + 1.0f / float(item->textureDepth())); shader->setUniformValue(shader->textureDimensions(), textureDimensions); - int sampleCount = qMax(item->textureWidth(), item->textureHeight()); - sampleCount = qMax(sampleCount, item->textureDepth()); + // Worst case scenario sample count + int sampleCount = item->textureWidth() + item->textureHeight() + + item->textureDepth(); shader->setUniformValue(shader->sampleCount(), sampleCount); - } m_drawer->drawObject(shader, item->mesh(), 0, 0, item->texture()); } else diff --git a/src/datavisualization/engine/shaders/texture3d.frag b/src/datavisualization/engine/shaders/texture3d.frag index 1192ae85..6dd7f78c 100644 --- a/src/datavisualization/engine/shaders/texture3d.frag +++ b/src/datavisualization/engine/shaders/texture3d.frag @@ -11,7 +11,10 @@ uniform highp int sampleCount; // This is the maximum sample count uniform highp float alphaMultiplier; uniform highp int preserveOpacity; -const highp float alphaThreshold = 0.0001; +// Ray traveling straight through a single 'alpha thickness' applies 100% of the encountered alpha. +// Rays traveling shorter distances apply a fraction. This is used to normalize the alpha over +// entire volume, regardless of texture dimensions +const highp float alphaThicknesses = 32.0; void main() { highp vec3 rayDir = -(cameraPositionRelativeToModel - pos); @@ -41,69 +44,122 @@ void main() { if (rayDir.z < 0) boxBounds.z = -1.0; highp vec3 t = (boxBounds - rayStart) * invRayDir; - highp float minT = min(t.x, t.y); - minT = min(minT, t.z); + highp float minT = min(t.x, min(t.y, t.z)); rayStop = rayStart + minT * rayDir; } + // Convert intersections to texture coords + rayStart = 0.5 * (rayStart + 1.0); + rayStop = 0.5 * (rayStop + 1.0); + + highp vec3 ray = rayStop - rayStart; + + // Avoid artifacts from divisions by zero + if (ray.x == 0) + ray.x = 0.000000001; + if (ray.y == 0) + ray.y = 0.000000001; + if (ray.z == 0) + ray.z = 0.000000001; + + highp vec3 absRay = abs(ray); + highp vec3 invAbsRay = 1.0 / absRay; + highp float fullDist = length(ray); + highp vec3 curPos = rayStart; + + highp vec4 curColor = vec4(0, 0, 0, 0); + highp float curAlpha = 0.0; + highp float curLen = 0.0; + highp vec3 curRgb = vec3(0, 0, 0); + highp vec4 destColor = vec4(0, 0, 0, 0); - highp float totalAlpha = 0.0; - - if (rayStart != rayStop) { - // Convert intersections to texture coords - rayStart = 0.5 * (rayStart + 1.0); - rayStop = 0.5 * (rayStop + 1.0); - - highp vec3 ray = rayStop - rayStart; - highp float fullDist = abs(length(ray)); - highp float rayX = abs(ray.x) * textureDimensions.x; - highp float rayY = abs(ray.y) * textureDimensions.y; - highp float rayZ = abs(ray.z) * textureDimensions.z; - highp float maxRayDim = max(rayX, rayY); - maxRayDim = max(maxRayDim, rayZ); - int maxCount = int(floor(maxRayDim)); - - highp vec3 step = ray / maxRayDim; - highp float stepSize = fullDist / maxRayDim; - - rayStart += (step * 0.001); - highp vec3 curPos = rayStart; - - // Adjust alpha multiplier according to the step size to get uniform alpha effect - // regardless of the ray angle. - highp float totalAlphaMultiplier = (stepSize / (1.0 / sampleCount)) * alphaMultiplier; - - highp vec4 curColor = vec4(0, 0, 0, 0); - highp vec3 curRgb = vec3(0, 0, 0); - highp float curAlpha = 0.0; - - // Raytrace into volume, need to sample pixels along the eye ray until we hit opacity 1 - for (int i = 0; i < sampleCount; i++) { - curColor = texture3D(textureSampler, curPos); - if (color8Bit != 0) - curColor = colorIndex[int(curColor.r * 255.0)]; - - // Unless we have explicit alpha multiplier, we want to preserve opacity anyway - if (curColor.a == 1.0 && (preserveOpacity != 0 || alphaMultiplier == 1.0)) + highp float totalOpacity = 1.0; + highp float nextOpacity = 1.0; + + highp float extraAlphaMultiplier = fullDist * alphaThicknesses; + + // nextEdges vector indicates the next edges of the texel boundaries along each axis that + // the ray is about to cross. The first edges are offset by a fraction of a texel to + // avoid artifacts from rounding errors later. + highp vec3 nextEdges = vec3(floor(curPos.x / textureDimensions.x) * textureDimensions.x, + floor(curPos.y / textureDimensions.y) * textureDimensions.y, + floor(curPos.z / textureDimensions.z) * textureDimensions.z); + + highp vec3 textureSteps = textureDimensions; + highp vec3 textureOffset = textureDimensions * 0.001; + if (ray.x > 0) { + nextEdges.x += textureDimensions.x + textureOffset.x; + } else { + nextEdges.x -= textureOffset.x; + textureSteps.x = -textureDimensions.x; + } + if (ray.y > 0) { + nextEdges.y += textureDimensions.y + textureOffset.y; + } else { + nextEdges.y -= textureOffset.y; + textureSteps.y = -textureDimensions.y; + } + if (ray.z > 0) { + nextEdges.z += textureDimensions.z + textureOffset.z; + } else { + nextEdges.z -= textureOffset.z; + textureSteps.z = -textureDimensions.z; + } + + // Raytrace into volume, need to sample pixels along the eye ray until we hit opacity 1 + for (int i = 0; i < sampleCount; i++) { + curColor = texture3D(textureSampler, curPos); + if (color8Bit != 0) + curColor = colorIndex[int(curColor.r * 255.0)]; + + // Find which dimension has least to go to figure out the next step distance + highp vec3 delta = abs(nextEdges - curPos); + highp vec3 modDelta = delta * invAbsRay; + highp float minDelta = min(modDelta.x, min(modDelta.y, modDelta.z)); + highp float stepSize; + if (minDelta == modDelta.x) { + nextEdges.x += textureSteps.x; + stepSize = delta.x * invAbsRay.x; + } + if (minDelta == modDelta.y) { + nextEdges.y += textureSteps.y; + stepSize = delta.y * invAbsRay.y; + } + if (minDelta == modDelta.z) { + nextEdges.z += textureSteps.z; + stepSize = delta.z * invAbsRay.z; + } + + curPos += stepSize * ray; + curLen += stepSize; + + if (curColor.a >= 0.0) { + curAlpha = alphaMultiplier * curColor.a; + if (curAlpha >= 1.0 || (curColor.a == 1.0 && preserveOpacity == 1)) curAlpha = 1.0; else - curAlpha = clamp(curColor.a * totalAlphaMultiplier, 0.0, 1.0); - - if (curAlpha > alphaThreshold) { - curRgb = curColor.rgb * curAlpha * (1.0 - totalAlpha); - destColor.rgb += curRgb; - totalAlpha += curAlpha; + curAlpha = curAlpha * extraAlphaMultiplier * stepSize; + highp float nextOpacity = totalOpacity - curAlpha; + // If opacity goes beyond full opacity, we need to adjust current alpha according + // to the fraction of the distance the material is visible, so that we don't get + // box artifacts around texels. + if (nextOpacity < 0.0) { + curAlpha *= totalOpacity / curAlpha; + nextOpacity = 0.0; } - if (i == maxCount || totalAlpha >= 1.0) - break; - curPos += step; + curRgb = curColor.rgb * curAlpha * (totalOpacity + nextOpacity); + totalOpacity = nextOpacity; + destColor.rgb += curRgb; } + + if (curLen >= 1.0 || totalOpacity <= 0.0) + break; } // Brighten up the final color if there is some transparency left - if (totalAlpha > alphaThreshold && totalAlpha < 1.0) - destColor *= 1.0 / totalAlpha; + if (totalOpacity >= 0.0 && totalOpacity < 1.0) + destColor *= (1.0 - (totalOpacity * 0.5)) / (1.0 - totalOpacity); - destColor.a = totalAlpha; + destColor.a = (1.0 - totalOpacity); gl_FragColor = clamp(destColor, 0.0, 1.0); } diff --git a/src/datavisualization/engine/shaders/texture3dslice.frag b/src/datavisualization/engine/shaders/texture3dslice.frag index 3d4c9030..00584744 100644 --- a/src/datavisualization/engine/shaders/texture3dslice.frag +++ b/src/datavisualization/engine/shaders/texture3dslice.frag @@ -14,8 +14,6 @@ const highp vec3 xPlaneNormal = vec3(1.0, 0, 0); const highp vec3 yPlaneNormal = vec3(0, 1.0, 0); const highp vec3 zPlaneNormal = vec3(0, 0, 1.0); -const highp float alphaThreshold = 0.0001; - void main() { // Find out where ray intersects the slice planes highp vec3 rayDir = -(cameraPositionRelativeToModel - pos); @@ -24,7 +22,7 @@ void main() { // Flip Y and Z so QImage bits work directly for texture and first image is in the front rayStart.yz = -rayStart.yz; rayDir.yz = -rayDir.yz; - highp float tFar = 2.0f; + highp float minT = 2.0f; if (rayDir.x != 0.0 && rayDir.y != 0.0 && rayDir.z != 0.0) { highp vec3 boxBounds = vec3(1.0, 1.0, 1.0); highp vec3 invRayDir = 1.0 / rayDir; @@ -35,24 +33,23 @@ void main() { if (rayDir.z < 0) boxBounds.z = -1.0; highp vec3 t = (boxBounds - rayStart) * invRayDir; - tFar = min(t.x, t.y); - tFar = min(tFar, t.z); + minT = min(t.x, min(t.y, t.z)); } highp vec3 xPoint = vec3(volumeSliceIndices.x, 0, 0); highp vec3 yPoint = vec3(0, volumeSliceIndices.y, 0); highp vec3 zPoint = vec3(0, 0, volumeSliceIndices.z); - highp float firstD = tFar + 1.0; + highp float firstD = minT + 1.0; highp float secondD = firstD; highp float thirdD = firstD; if (volumeSliceIndices.x >= -1.0) { highp float dx = dot(xPoint - rayStart, xPlaneNormal) / dot(rayDir, xPlaneNormal); - if (dx >= 0.0 && dx <= tFar) + if (dx >= 0.0 && dx <= minT) firstD = min(dx, firstD); } if (volumeSliceIndices.y >= -1.0) { highp float dy = dot(yPoint - rayStart, yPlaneNormal) / dot(rayDir, yPlaneNormal); - if (dy >= 0.0 && dy <= tFar) { + if (dy >= 0.0 && dy <= minT) { if (dy < firstD) { secondD = firstD; firstD = dy; @@ -64,7 +61,7 @@ void main() { if (volumeSliceIndices.z >= -1.0) { highp float dz = dot(zPoint - rayStart, zPlaneNormal) / dot(rayDir, zPlaneNormal); if (dz >= 0.0) { - if (dz < firstD && dz <= tFar) { + if (dz < firstD && dz <= minT) { thirdD = secondD; secondD = firstD; firstD = dz; @@ -85,14 +82,14 @@ void main() { // Convert intersection to texture coords - if (firstD <= tFar) { + if (firstD <= minT) { highp vec3 firstTex = rayStart + rayDir * firstD; firstTex = 0.5 * (firstTex + 1.0); curColor = texture3D(textureSampler, firstTex); if (color8Bit != 0) curColor = colorIndex[int(curColor.r * 255.0)]; - if (curColor.a > alphaThreshold) { + if (curColor.a > 0.0) { curAlpha = curColor.a; if (curColor.a == 1.0 && preserveOpacity != 0) curAlpha = 1.0; @@ -101,13 +98,13 @@ void main() { destColor.rgb = curColor.rgb * curAlpha; totalAlpha = curAlpha; } - if (secondD <= tFar && totalAlpha < 1.0) { + if (secondD <= minT && totalAlpha < 1.0) { highp vec3 secondTex = rayStart + rayDir * secondD; secondTex = 0.5 * (secondTex + 1.0); curColor = texture3D(textureSampler, secondTex); if (color8Bit != 0) curColor = colorIndex[int(curColor.r * 255.0)]; - if (curColor.a > alphaThreshold) { + if (curColor.a > 0.0) { if (curColor.a == 1.0 && preserveOpacity != 0) curAlpha = 1.0; else @@ -116,11 +113,11 @@ void main() { destColor.rgb += curRgb; totalAlpha += curAlpha; } - if (thirdD <= tFar && totalAlpha < 1.0) { + if (thirdD <= minT && totalAlpha < 1.0) { highp vec3 thirdTex = rayStart + rayDir * thirdD; thirdTex = 0.5 * (thirdTex + 1.0); curColor = texture3D(textureSampler, thirdTex); - if (curColor.a > alphaThreshold) { + if (curColor.a > 0.0) { if (color8Bit != 0) curColor = colorIndex[int(curColor.r * 255.0)]; if (curColor.a == 1.0 && preserveOpacity != 0) @@ -136,7 +133,7 @@ void main() { } // Brighten up the final color if there is some transparency left - if (totalAlpha > alphaThreshold && totalAlpha < 1.0) + if (totalAlpha > 0.0 && totalAlpha < 1.0) destColor *= 1.0 / totalAlpha; destColor.a = totalAlpha; -- cgit v1.2.3 From 3c2d0623c74cc51d0c3b73e0b56b5f5052676ee4 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Mon, 1 Sep 2014 14:45:05 +0300 Subject: Add option to use low definition volume shader. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Low definition volume shader samples the texture at even intervals, so it will sometimes skip texels, causing flickering. Change-Id: Iee6cb0e8893498b89ce1a40f34701a53d100283e Reviewed-by: Tomi Korpipää --- examples/datavisualization/volumetric/main.cpp | 9 +- .../datavisualization/volumetric/volumetric.cpp | 5 + examples/datavisualization/volumetric/volumetric.h | 1 + src/datavisualization/data/customrenderitem.cpp | 3 +- src/datavisualization/data/customrenderitem_p.h | 3 + src/datavisualization/data/qcustom3dvolume.cpp | 58 +++++++++- src/datavisualization/data/qcustom3dvolume.h | 5 + src/datavisualization/data/qcustom3dvolume_p.h | 5 +- .../engine/abstract3drenderer.cpp | 67 ++++++++---- .../engine/abstract3drenderer_p.h | 3 +- src/datavisualization/engine/bars3drenderer.cpp | 8 +- src/datavisualization/engine/engine.qrc | 1 + src/datavisualization/engine/scatter3drenderer.cpp | 7 +- .../engine/shaders/texture3d.frag | 8 +- .../engine/shaders/texture3dlowdef.frag | 119 +++++++++++++++++++++ src/datavisualization/engine/surface3drenderer.cpp | 10 +- tests/volumetrictest/volumetrictest.cpp | 4 + 17 files changed, 265 insertions(+), 51 deletions(-) create mode 100644 src/datavisualization/engine/shaders/texture3dlowdef.frag diff --git a/examples/datavisualization/volumetric/main.cpp b/examples/datavisualization/volumetric/main.cpp index 84062969..68895b17 100644 --- a/examples/datavisualization/volumetric/main.cpp +++ b/examples/datavisualization/volumetric/main.cpp @@ -135,6 +135,10 @@ int main(int argc, char **argv) preserveOpacityCheckBox->setText(QStringLiteral("Preserve opacity")); preserveOpacityCheckBox->setChecked(true); + QCheckBox *useHighDefShaderCheckBox = new QCheckBox(widget); + useHighDefShaderCheckBox->setText(QStringLiteral("Use HD shader")); + useHighDefShaderCheckBox->setChecked(true); + vLayout->addWidget(sliceXCheckBox); vLayout->addWidget(sliceXSlider); vLayout->addWidget(sliceImageXLabel); @@ -150,7 +154,8 @@ int main(int argc, char **argv) vLayout2->addWidget(colorTableCheckBox); vLayout2->addWidget(alphaMultiplierLabel); vLayout2->addWidget(alphaMultiplierSlider); - vLayout2->addWidget(preserveOpacityCheckBox, 1, Qt::AlignTop); + vLayout2->addWidget(preserveOpacityCheckBox); + vLayout2->addWidget(useHighDefShaderCheckBox, 1, Qt::AlignTop); VolumetricModifier *modifier = new VolumetricModifier(graph); modifier->setFpsLabel(fpsLabel); @@ -184,6 +189,8 @@ int main(int argc, char **argv) &VolumetricModifier::changeColorTable); QObject::connect(preserveOpacityCheckBox, &QCheckBox::stateChanged, modifier, &VolumetricModifier::setPreserveOpacity); + QObject::connect(useHighDefShaderCheckBox, &QCheckBox::stateChanged, modifier, + &VolumetricModifier::setUseHighDefShader); QObject::connect(alphaMultiplierSlider, &QSlider::valueChanged, modifier, &VolumetricModifier::adjustAlphaMultiplier); diff --git a/examples/datavisualization/volumetric/volumetric.cpp b/examples/datavisualization/volumetric/volumetric.cpp index 56f02dcb..1de4b1b7 100644 --- a/examples/datavisualization/volumetric/volumetric.cpp +++ b/examples/datavisualization/volumetric/volumetric.cpp @@ -338,6 +338,11 @@ void VolumetricModifier::setPreserveOpacity(bool enabled) adjustSliceZ(m_sliceSliderZ->value()); } +void VolumetricModifier::setUseHighDefShader(bool enabled) +{ + m_volumeItem->setUseHighDefShader(enabled); +} + void VolumetricModifier::adjustAlphaMultiplier(int value) { float mult; diff --git a/examples/datavisualization/volumetric/volumetric.h b/examples/datavisualization/volumetric/volumetric.h index 497506ad..83af4b3c 100644 --- a/examples/datavisualization/volumetric/volumetric.h +++ b/examples/datavisualization/volumetric/volumetric.h @@ -60,6 +60,7 @@ public slots: void setSliceSliders(QSlider *sliderX, QSlider *sliderY, QSlider *sliderZ); void changeColorTable(int enabled); void setPreserveOpacity(bool enabled); + void setUseHighDefShader(bool enabled); void adjustAlphaMultiplier(int value); private: diff --git a/src/datavisualization/data/customrenderitem.cpp b/src/datavisualization/data/customrenderitem.cpp index c316fd38..555f48b7 100644 --- a/src/datavisualization/data/customrenderitem.cpp +++ b/src/datavisualization/data/customrenderitem.cpp @@ -42,7 +42,8 @@ CustomRenderItem::CustomRenderItem() m_sliceIndexY(-1), m_sliceIndexZ(-1), m_alphaMultiplier(1.0f), - m_preserveOpacity(true) + m_preserveOpacity(true), + m_useHighDefShader(true) { } diff --git a/src/datavisualization/data/customrenderitem_p.h b/src/datavisualization/data/customrenderitem_p.h index 5428ce43..5024270a 100644 --- a/src/datavisualization/data/customrenderitem_p.h +++ b/src/datavisualization/data/customrenderitem_p.h @@ -96,6 +96,8 @@ public: inline float alphaMultiplier() const { return m_alphaMultiplier; } inline void setPreserveOpacity(bool enable) { m_preserveOpacity = enable; } inline bool preserveOpacity() const { return m_preserveOpacity; } + inline void setUseHighDefShader(bool enable) { m_useHighDefShader = enable; } + inline bool useHighDefShader() const {return m_useHighDefShader; } private: Q_DISABLE_COPY(CustomRenderItem) @@ -126,6 +128,7 @@ private: int m_sliceIndexZ; float m_alphaMultiplier; bool m_preserveOpacity; + bool m_useHighDefShader; }; typedef QHash CustomRenderItemArray; diff --git a/src/datavisualization/data/qcustom3dvolume.cpp b/src/datavisualization/data/qcustom3dvolume.cpp index 503d324d..789b7718 100644 --- a/src/datavisualization/data/qcustom3dvolume.cpp +++ b/src/datavisualization/data/qcustom3dvolume.cpp @@ -135,6 +135,24 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION * \sa alphaMultiplier */ +/*! + * \qmlproperty bool Custom3DVolume::useHighDefShader + * + * If this property value is \c{true}, a high definition shader is used to render the volume. + * If it is \c{false}, a low definition shader is used. + * + * The high definition shader guarantees that every visible texel of the volume texture is sampled + * when the volume is rendered. + * The low definition shader renders only a rough approximation of the volume contents, + * but at much higher frame rate. The low definition shader doesn't guarantee every texel of the + * volume texture is sampled, so there may be flickering if the volume contains distinct thin + * features. + * + * \note This value doesn't affect the level of detail when rendering the slices of the volume. + * + * Defaults to \c{true}. + */ + /*! * Constructs QCustom3DVolume with given \a parent. */ @@ -672,6 +690,40 @@ bool QCustom3DVolume::preserveOpacity() const return dptrc()->m_preserveOpacity; } +/*! + * \property QCustom3DVolume::useHighDefShader + * + * If this property value is \c{true}, a high definition shader is used to render the volume. + * If it is \c{false}, a low definition shader is used. + * + * The high definition shader guarantees that every visible texel of the volume texture is sampled + * when the volume is rendered. + * The low definition shader renders only a rough approximation of the volume contents, + * but at much higher frame rate. The low definition shader doesn't guarantee every texel of the + * volume texture is sampled, so there may be flickering if the volume contains distinct thin + * features. + * + * \note This value doesn't affect the level of detail when rendering the slices of the volume. + * + * Defaults to \c{true}. + * + * \sa renderSlice() + */ +void QCustom3DVolume::setUseHighDefShader(bool enable) +{ + if (dptr()->m_useHighDefShader != enable) { + dptr()->m_useHighDefShader = enable; + dptr()->m_dirtyBitsVolume.shaderDirty = true; + emit useHighDefShaderChanged(enable); + emit dptr()->needUpdate(); + } +} + +bool QCustom3DVolume::useHighDefShader() const +{ + return dptrc()->m_useHighDefShader; +} + /*! * Renders the slice specified by \a index along \a axis into an image. * The texture format of this object is used. @@ -712,7 +764,8 @@ QCustom3DVolumePrivate::QCustom3DVolumePrivate(QCustom3DVolume *q) : m_textureFormat(QImage::Format_ARGB32), m_textureData(0), m_alphaMultiplier(1.0f), - m_preserveOpacity(true) + m_preserveOpacity(true), + m_useHighDefShader(true) { m_isVolumeItem = true; m_meshFile = QStringLiteral(":/defaultMeshes/barFull"); @@ -736,7 +789,8 @@ QCustom3DVolumePrivate::QCustom3DVolumePrivate(QCustom3DVolume *q, const QVector m_colorTable(colorTable), m_textureData(textureData), m_alphaMultiplier(1.0f), - m_preserveOpacity(true) + m_preserveOpacity(true), + m_useHighDefShader(true) { m_isVolumeItem = true; m_shadowCasting = false; diff --git a/src/datavisualization/data/qcustom3dvolume.h b/src/datavisualization/data/qcustom3dvolume.h index 8627eb71..23ae07d9 100644 --- a/src/datavisualization/data/qcustom3dvolume.h +++ b/src/datavisualization/data/qcustom3dvolume.h @@ -41,6 +41,7 @@ class QT_DATAVISUALIZATION_EXPORT QCustom3DVolume : public QCustom3DItem Q_PROPERTY(QVector *textureData READ textureData WRITE setTextureData NOTIFY textureDataChanged) Q_PROPERTY(float alphaMultiplier READ alphaMultiplier WRITE setAlphaMultiplier NOTIFY alphaMultiplierChanged) Q_PROPERTY(bool preserveOpacity READ preserveOpacity WRITE setPreserveOpacity NOTIFY preserveOpacityChanged) + Q_PROPERTY(bool useHighDefShader READ useHighDefShader WRITE setUseHighDefShader NOTIFY useHighDefShaderChanged) public: @@ -86,6 +87,9 @@ public: void setPreserveOpacity(bool enable); bool preserveOpacity() const; + void setUseHighDefShader(bool enable); + bool useHighDefShader() const; + QImage renderSlice(Qt::Axis axis, int index); signals: @@ -100,6 +104,7 @@ signals: void textureFormatChanged(QImage::Format format); void alphaMultiplierChanged(float mult); void preserveOpacityChanged(bool enabled); + void useHighDefShaderChanged(bool enabled); protected: QCustom3DVolumePrivate *dptr(); diff --git a/src/datavisualization/data/qcustom3dvolume_p.h b/src/datavisualization/data/qcustom3dvolume_p.h index b83e27fb..8b0b439e 100644 --- a/src/datavisualization/data/qcustom3dvolume_p.h +++ b/src/datavisualization/data/qcustom3dvolume_p.h @@ -41,6 +41,7 @@ struct QCustomVolumeDirtyBitField { bool textureDataDirty : 1; bool textureFormatDirty : 1; bool alphaDirty : 1; + bool shaderDirty : 1; QCustomVolumeDirtyBitField() : textureDimensionsDirty(false), @@ -48,7 +49,8 @@ struct QCustomVolumeDirtyBitField { colorTableDirty(false), textureDataDirty(false), textureFormatDirty(false), - alphaDirty(false) + alphaDirty(false), + shaderDirty(false) { } }; @@ -84,6 +86,7 @@ public: float m_alphaMultiplier; bool m_preserveOpacity; + bool m_useHighDefShader; QCustomVolumeDirtyBitField m_dirtyBitsVolume; diff --git a/src/datavisualization/engine/abstract3drenderer.cpp b/src/datavisualization/engine/abstract3drenderer.cpp index 78f4b600..f031a34c 100644 --- a/src/datavisualization/engine/abstract3drenderer.cpp +++ b/src/datavisualization/engine/abstract3drenderer.cpp @@ -60,6 +60,7 @@ Abstract3DRenderer::Abstract3DRenderer(Abstract3DController *controller) m_visibleSeriesCount(0), m_customItemShader(0), m_volumeTextureShader(0), + m_volumeTextureLowDefShader(0), m_volumeTextureSliceShader(0), m_useOrthoProjection(false), m_xFlipped(false), @@ -103,6 +104,7 @@ Abstract3DRenderer::~Abstract3DRenderer() delete m_selectionLabelItem; delete m_customItemShader; delete m_volumeTextureShader; + delete m_volumeTextureLowDefShader; delete m_volumeTextureSliceShader; foreach (SeriesRenderCache *cache, m_renderCacheList) { @@ -198,22 +200,27 @@ void Abstract3DRenderer::initGradientShaders(const QString &vertexShader, void Abstract3DRenderer::initCustomItemShaders(const QString &vertexShader, const QString &fragmentShader) { - if (m_customItemShader) - delete m_customItemShader; + delete m_customItemShader; m_customItemShader = new ShaderHelper(this, vertexShader, fragmentShader); m_customItemShader->initialize(); } void Abstract3DRenderer::initVolumeTextureShaders(const QString &vertexShader, const QString &fragmentShader, + const QString &fragmentLowDefShader, const QString &sliceShader) { - if (m_volumeTextureShader) - delete m_volumeTextureShader; + + delete m_volumeTextureShader; m_volumeTextureShader = new ShaderHelper(this, vertexShader, fragmentShader); m_volumeTextureShader->initialize(); - if (m_volumeTextureSliceShader) - delete m_volumeTextureSliceShader; + + delete m_volumeTextureLowDefShader; + m_volumeTextureLowDefShader = new ShaderHelper(this, vertexShader, + fragmentLowDefShader); + m_volumeTextureLowDefShader->initialize(); + + delete m_volumeTextureSliceShader; m_volumeTextureSliceShader = new ShaderHelper(this, vertexShader, sliceShader); m_volumeTextureSliceShader->initialize(); } @@ -319,6 +326,7 @@ void Abstract3DRenderer::reInitShaders() } initVolumeTextureShaders(QStringLiteral(":/shaders/vertexTexture3D"), QStringLiteral(":/shaders/fragmentTexture3D"), + QStringLiteral(":/shaders/fragmentTexture3DLowDef"), QStringLiteral(":/shaders/fragmentTexture3DSlice")); #else initGradientShaders(QStringLiteral(":/shaders/vertex"), @@ -988,6 +996,7 @@ CustomRenderItem *Abstract3DRenderer::addCustomItem(QCustom3DItem *item) newItem->setSliceIndexZ(volumeItem->sliceIndexZ()); newItem->setAlphaMultiplier(volumeItem->alphaMultiplier()); newItem->setPreserveOpacity(volumeItem->preserveOpacity()); + newItem->setUseHighDefShader(volumeItem->useHighDefShader()); #endif } newItem->setScaling(scaling); @@ -1068,15 +1077,15 @@ void Abstract3DRenderer::updateCustomItem(CustomRenderItem *renderItem) } } else #if !defined(QT_OPENGL_ES_2) - if (!item->d_ptr->m_isVolumeItem) + if (!item->d_ptr->m_isVolumeItem) #endif - { - renderItem->setBlendNeeded(textureImage.hasAlphaChannel()); - GLuint oldTexture = renderItem->texture(); - m_textureHelper->deleteTexture(&oldTexture); - GLuint texture = m_textureHelper->create2DTexture(textureImage, true, true, true); - renderItem->setTexture(texture); - } + { + renderItem->setBlendNeeded(textureImage.hasAlphaChannel()); + GLuint oldTexture = renderItem->texture(); + m_textureHelper->deleteTexture(&oldTexture); + GLuint texture = m_textureHelper->create2DTexture(textureImage, true, true, true); + renderItem->setTexture(texture); + } item->d_ptr->clearTextureImage(); item->d_ptr->m_dirtyBits.textureDirty = false; } @@ -1140,6 +1149,10 @@ void Abstract3DRenderer::updateCustomItem(CustomRenderItem *renderItem) renderItem->setPreserveOpacity(volumeItem->preserveOpacity()); volumeItem->dptr()->m_dirtyBitsVolume.alphaDirty = false; } + if (volumeItem->dptr()->m_dirtyBitsVolume.shaderDirty) { + renderItem->setUseHighDefShader(volumeItem->useHighDefShader()); + volumeItem->dptr()->m_dirtyBitsVolume.shaderDirty = false; + } #endif } } @@ -1155,8 +1168,6 @@ void Abstract3DRenderer::updateCustomItemPositions() void Abstract3DRenderer::drawCustomItems(RenderingState state, ShaderHelper *regularShader, - ShaderHelper *volumeShader, - ShaderHelper *volumeSliceShader, const QMatrix4x4 &viewMatrix, const QMatrix4x4 &projectionViewMatrix, const QMatrix4x4 &depthProjectionViewMatrix, @@ -1254,9 +1265,11 @@ void Abstract3DRenderer::drawCustomItems(RenderingState state, if (item->sliceIndexX() >= 0 || item->sliceIndexY() >= 0 || item->sliceIndexZ() >= 0) { - shader = volumeSliceShader; + shader = m_volumeTextureSliceShader; + } else if (item->useHighDefShader()) { + shader = m_volumeTextureShader; } else { - shader = volumeShader; + shader = m_volumeTextureLowDefShader; } } else { shader = regularShader; @@ -1309,7 +1322,7 @@ void Abstract3DRenderer::drawCustomItems(RenderingState state, shader->setUniformValue(shader->alphaMultiplier(), item->alphaMultiplier()); shader->setUniformValue(shader->preserveOpacity(), item->preserveOpacity() ? 1 : 0); - if (shader == volumeSliceShader) { + if (shader == m_volumeTextureSliceShader) { QVector3D slices((float(item->sliceIndexX()) + 0.5f) / float(item->textureWidth()) * 2.0 - 1.0, (float(item->sliceIndexY()) + 0.5f) @@ -1323,11 +1336,21 @@ void Abstract3DRenderer::drawCustomItems(RenderingState state, QVector3D textureDimensions(1.0f / float(item->textureWidth()), 1.0f / float(item->textureHeight()), 1.0f / float(item->textureDepth())); - shader->setUniformValue(shader->textureDimensions(), textureDimensions); // Worst case scenario sample count - int sampleCount = item->textureWidth() + item->textureHeight() - + item->textureDepth(); + int sampleCount; + if (shader == m_volumeTextureLowDefShader) { + sampleCount = qMax(item->textureWidth(), + qMax(item->textureDepth(), item->textureHeight())); + // Further improve speed with big textures by simply dropping every + // other sample: + if (sampleCount > 256) + sampleCount /= 2; + } else { + sampleCount = item->textureWidth() + item->textureHeight() + + item->textureDepth(); + } + shader->setUniformValue(shader->textureDimensions(), textureDimensions); shader->setUniformValue(shader->sampleCount(), sampleCount); } m_drawer->drawObject(shader, item->mesh(), 0, 0, item->texture()); diff --git a/src/datavisualization/engine/abstract3drenderer_p.h b/src/datavisualization/engine/abstract3drenderer_p.h index 1e815463..83b4e1c7 100644 --- a/src/datavisualization/engine/abstract3drenderer_p.h +++ b/src/datavisualization/engine/abstract3drenderer_p.h @@ -94,6 +94,7 @@ public: const QString &fragmentShader); virtual void initVolumeTextureShaders(const QString &vertexShader, const QString &fragmentShader, + const QString &fragmentLowDefShader, const QString &sliceShader); virtual void updateAxisType(QAbstract3DAxis::AxisOrientation orientation, QAbstract3DAxis::AxisType type); @@ -146,7 +147,6 @@ public: QString &selectionLabel(); void drawCustomItems(RenderingState state, ShaderHelper *regularShader, - ShaderHelper *volumeShader, ShaderHelper *volumeSliceShader, const QMatrix4x4 &viewMatrix, const QMatrix4x4 &projectionViewMatrix, const QMatrix4x4 &depthProjectionViewMatrix, @@ -243,6 +243,7 @@ protected: ShaderHelper *m_customItemShader; ShaderHelper *m_volumeTextureShader; + ShaderHelper *m_volumeTextureLowDefShader; ShaderHelper *m_volumeTextureSliceShader; bool m_useOrthoProjection; diff --git a/src/datavisualization/engine/bars3drenderer.cpp b/src/datavisualization/engine/bars3drenderer.cpp index 22bf6b87..1614b563 100644 --- a/src/datavisualization/engine/bars3drenderer.cpp +++ b/src/datavisualization/engine/bars3drenderer.cpp @@ -1090,8 +1090,7 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) } } - Abstract3DRenderer::drawCustomItems(RenderingDepth, m_depthShader, m_volumeTextureShader, - m_volumeTextureSliceShader, viewMatrix, + Abstract3DRenderer::drawCustomItems(RenderingDepth, m_depthShader, viewMatrix, projectionViewMatrix, depthProjectionViewMatrix, m_depthTexture, m_shadowQualityToShader); @@ -1179,7 +1178,6 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) } glCullFace(GL_BACK); Abstract3DRenderer::drawCustomItems(RenderingSelection, m_selectionShader, - m_volumeTextureShader, m_volumeTextureSliceShader, viewMatrix, projectionViewMatrix, depthProjectionViewMatrix, m_depthTexture, m_shadowQualityToShader); @@ -1234,7 +1232,6 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) startBar, stopBar, stepBar, -1.0f); Abstract3DRenderer::drawCustomItems(RenderingNormal, m_customItemShader, - m_volumeTextureShader, m_volumeTextureSliceShader, viewMatrix, projectionViewMatrix, depthProjectionViewMatrix, m_depthTexture, m_shadowQualityToShader, -1.0f); @@ -1272,8 +1269,7 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) drawGridLines(depthProjectionViewMatrix, projectionViewMatrix, viewMatrix); // Draw custom items - Abstract3DRenderer::drawCustomItems(RenderingNormal, m_customItemShader, m_volumeTextureShader, - m_volumeTextureSliceShader, viewMatrix, + Abstract3DRenderer::drawCustomItems(RenderingNormal, m_customItemShader, viewMatrix, projectionViewMatrix, depthProjectionViewMatrix, m_depthTexture, m_shadowQualityToShader); diff --git a/src/datavisualization/engine/engine.qrc b/src/datavisualization/engine/engine.qrc index 88440bec..0936bc16 100644 --- a/src/datavisualization/engine/engine.qrc +++ b/src/datavisualization/engine/engine.qrc @@ -64,5 +64,6 @@ shaders/texture3dslice.frag shaders/shadowNoMatrices.vert shaders/defaultNoMatrices.vert + shaders/texture3dlowdef.frag diff --git a/src/datavisualization/engine/scatter3drenderer.cpp b/src/datavisualization/engine/scatter3drenderer.cpp index ca983dac..8c4644c6 100644 --- a/src/datavisualization/engine/scatter3drenderer.cpp +++ b/src/datavisualization/engine/scatter3drenderer.cpp @@ -575,8 +575,7 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) } } - Abstract3DRenderer::drawCustomItems(RenderingDepth, m_depthShader, m_volumeTextureShader, - m_volumeTextureSliceShader, viewMatrix, + Abstract3DRenderer::drawCustomItems(RenderingDepth, m_depthShader, viewMatrix, projectionViewMatrix, depthProjectionViewMatrix, m_depthTexture, m_shadowQualityToShader); @@ -681,7 +680,6 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) } Abstract3DRenderer::drawCustomItems(RenderingSelection, m_selectionShader, - m_volumeTextureShader, m_volumeTextureSliceShader, viewMatrix, projectionViewMatrix, depthProjectionViewMatrix, m_depthTexture, m_shadowQualityToShader); @@ -1455,8 +1453,7 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) } } - Abstract3DRenderer::drawCustomItems(RenderingNormal, m_customItemShader, m_volumeTextureShader, - m_volumeTextureSliceShader, viewMatrix, + Abstract3DRenderer::drawCustomItems(RenderingNormal, m_customItemShader, viewMatrix, projectionViewMatrix, depthProjectionViewMatrix, m_depthTexture, m_shadowQualityToShader); diff --git a/src/datavisualization/engine/shaders/texture3d.frag b/src/datavisualization/engine/shaders/texture3d.frag index 6dd7f78c..460cbcc6 100644 --- a/src/datavisualization/engine/shaders/texture3d.frag +++ b/src/datavisualization/engine/shaders/texture3d.frag @@ -74,9 +74,8 @@ void main() { highp vec4 destColor = vec4(0, 0, 0, 0); highp float totalOpacity = 1.0; - highp float nextOpacity = 1.0; - highp float extraAlphaMultiplier = fullDist * alphaThicknesses; + highp float extraAlphaMultiplier = fullDist * alphaThicknesses * alphaMultiplier; // nextEdges vector indicates the next edges of the texel boundaries along each axis that // the ray is about to cross. The first edges are offset by a fraction of a texel to @@ -134,11 +133,10 @@ void main() { curLen += stepSize; if (curColor.a >= 0.0) { - curAlpha = alphaMultiplier * curColor.a; - if (curAlpha >= 1.0 || (curColor.a == 1.0 && preserveOpacity == 1)) + if (curColor.a == 1.0 && (preserveOpacity == 1 || alphaMultiplier >= 1.0)) curAlpha = 1.0; else - curAlpha = curAlpha * extraAlphaMultiplier * stepSize; + curAlpha = curColor.a * extraAlphaMultiplier * stepSize; highp float nextOpacity = totalOpacity - curAlpha; // If opacity goes beyond full opacity, we need to adjust current alpha according // to the fraction of the distance the material is visible, so that we don't get diff --git a/src/datavisualization/engine/shaders/texture3dlowdef.frag b/src/datavisualization/engine/shaders/texture3dlowdef.frag new file mode 100644 index 00000000..72b959fc --- /dev/null +++ b/src/datavisualization/engine/shaders/texture3dlowdef.frag @@ -0,0 +1,119 @@ +#version 120 + +varying highp vec3 pos; + +uniform highp sampler3D textureSampler; +uniform highp vec3 cameraPositionRelativeToModel; +uniform highp vec4 colorIndex[256]; +uniform highp int color8Bit; +uniform highp vec3 textureDimensions; +uniform highp int sampleCount; // This is the maximum sample count +uniform highp float alphaMultiplier; +uniform highp int preserveOpacity; + +// Ray traveling straight through a single 'alpha thickness' applies 100% of the encountered alpha. +// Rays traveling shorter distances apply a fraction. This is used to normalize the alpha over +// entire volume, regardless of texture dimensions +const highp float alphaThicknesses = 32.0; +const highp float SQRT3 = 1.73205081; + +void main() { + highp vec3 rayDir = -(cameraPositionRelativeToModel - pos); + vec3 rayStart = pos; + // Flip Y and Z so QImage bits work directly for texture and first image is in the front + rayDir.yz = -rayDir.yz; + rayStart.yz = -rayStart.yz; + + // Calculate ray intersection endpoint + vec3 rayStop; + if (rayDir.x == 0.0) { + rayStop.yz = rayStart.yz; + rayStop.x = -rayStart.x; + } else if (rayDir.y == 0.0) { + rayStop.xz = rayStart.xz; + rayStop.y = -rayStart.y; + } else if (rayDir.z == 0.0) { + rayStop.xy = rayStart.xy; + rayStop.z = -rayStart.z; + } else { + highp vec3 boxBounds = vec3(1.0, 1.0, 1.0); + highp vec3 invRayDir = 1.0 / rayDir; + if (rayDir.x < 0) + boxBounds.x = -1.0; + if (rayDir.y < 0) + boxBounds.y = -1.0; + if (rayDir.z < 0) + boxBounds.z = -1.0; + highp vec3 t = (boxBounds - rayStart) * invRayDir; + highp float minT = min(t.x, min(t.y, t.z)); + rayStop = rayStart + minT * rayDir; + } + + // Convert intersections to texture coords + rayStart = 0.5 * (rayStart + 1.0); + rayStop = 0.5 * (rayStop + 1.0); + + highp vec3 ray = rayStop - rayStart; + + // Avoid artifacts from divisions by zero + if (ray.x == 0) + ray.x = 0.000000001; + if (ray.y == 0) + ray.y = 0.000000001; + if (ray.z == 0) + ray.z = 0.000000001; + + + highp float fullDist = length(ray); + highp float stepSize = SQRT3 / sampleCount; + highp vec3 step = (SQRT3 * normalize(ray)) / sampleCount; + + rayStart += (step * 0.001); + + highp vec3 curPos = rayStart; + highp float curLen = 0.0; + highp vec4 curColor = vec4(0, 0, 0, 0); + highp float curAlpha = 0.0; + highp vec3 curRgb = vec3(0, 0, 0); + highp vec4 destColor = vec4(0, 0, 0, 0); + highp float totalOpacity = 1.0; + + highp float extraAlphaMultiplier = stepSize * alphaThicknesses * alphaMultiplier; + + // Raytrace into volume, need to sample pixels along the eye ray until we hit opacity 1 + for (int i = 0; i < sampleCount; i++) { + curColor = texture3D(textureSampler, curPos); + if (color8Bit != 0) + curColor = colorIndex[int(curColor.r * 255.0)]; + + if (curColor.a >= 0.0) { + if (curColor.a == 1.0 && (preserveOpacity == 1 || alphaMultiplier >= 1.0)) + curAlpha = 1.0; + else + curAlpha = curColor.a * extraAlphaMultiplier; + highp float nextOpacity = totalOpacity - curAlpha; + // If opacity goes beyond full opacity, we need to adjust current alpha according + // to the fraction of the distance the material is visible, so that we don't get + // box artifacts around texels. + if (nextOpacity < 0.0) { + curAlpha *= totalOpacity / curAlpha; + nextOpacity = 0.0; + } + + curRgb = curColor.rgb * curAlpha * (totalOpacity + nextOpacity); + destColor.rgb += curRgb; + totalOpacity = nextOpacity; + } + curPos += step; + curLen += stepSize; + if (curLen >= fullDist || totalOpacity <= 0.0) + break; + } + + // Brighten up the final color if there is some transparency left + if (totalOpacity >= 0.0 && totalOpacity < 1.0) + destColor *= (1.0 - (totalOpacity * 0.5)) / (1.0 - totalOpacity); + + destColor.a = (1.0 - totalOpacity); + gl_FragColor = clamp(destColor, 0.0, 1.0); +} diff --git a/src/datavisualization/engine/surface3drenderer.cpp b/src/datavisualization/engine/surface3drenderer.cpp index 58f97b0a..ced4c789 100644 --- a/src/datavisualization/engine/surface3drenderer.cpp +++ b/src/datavisualization/engine/surface3drenderer.cpp @@ -1196,8 +1196,7 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) glEnable(GL_CULL_FACE); glCullFace(GL_FRONT); - Abstract3DRenderer::drawCustomItems(RenderingDepth, m_depthShader, m_volumeTextureShader, - m_volumeTextureSliceShader, viewMatrix, + Abstract3DRenderer::drawCustomItems(RenderingDepth, m_depthShader, viewMatrix, projectionViewMatrix, depthProjectionViewMatrix, m_depthTexture, m_shadowQualityToShader); @@ -1234,8 +1233,7 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) glDisableVertexAttribArray(m_depthShader->posAtt()); - Abstract3DRenderer::drawCustomItems(RenderingDepth, m_depthShader, m_volumeTextureShader, - m_volumeTextureSliceShader, viewMatrix, + Abstract3DRenderer::drawCustomItems(RenderingDepth, m_depthShader, viewMatrix, projectionViewMatrix, depthProjectionViewMatrix, m_depthTexture, m_shadowQualityToShader); @@ -1292,7 +1290,6 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) } m_surfaceGridShader->bind(); Abstract3DRenderer::drawCustomItems(RenderingSelection, m_surfaceGridShader, - m_volumeTextureShader, m_volumeTextureSliceShader, viewMatrix, projectionViewMatrix, depthProjectionViewMatrix, m_depthTexture, m_shadowQualityToShader); @@ -1864,8 +1861,7 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) } } - Abstract3DRenderer::drawCustomItems(RenderingNormal, m_customItemShader, m_volumeTextureShader, - m_volumeTextureSliceShader, viewMatrix, + Abstract3DRenderer::drawCustomItems(RenderingNormal, m_customItemShader, viewMatrix, projectionViewMatrix, depthProjectionViewMatrix, m_depthTexture, m_shadowQualityToShader); diff --git a/tests/volumetrictest/volumetrictest.cpp b/tests/volumetrictest/volumetrictest.cpp index 554373cb..9c89e761 100644 --- a/tests/volumetrictest/volumetrictest.cpp +++ b/tests/volumetrictest/volumetrictest.cpp @@ -51,6 +51,10 @@ VolumetricModifier::VolumetricModifier(Q3DScatter *scatter) createAnotherVolume(); createYetAnotherVolume(); +// m_volumeItem->setUseHighDefShader(false); +// m_volumeItem2->setUseHighDefShader(false); +// m_volumeItem3->setUseHighDefShader(false); + m_plainItem = new QCustom3DItem; QImage texture(2, 2, QImage::Format_ARGB32); texture.fill(QColor(200, 200, 200, 130)); -- cgit v1.2.3 From 57726e6752eacbbfee906479be9e1c49d456fb6f Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Tue, 2 Sep 2014 15:10:53 +0300 Subject: Change volumetric example to show fictional terrain MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I7cb3c59acef5364ff964da3e756d2db387b53308 Reviewed-by: Mika Salmela Reviewed-by: Tomi Korpipää --- .../datavisualization/volumetric/layer_ground.png | Bin 0 -> 63473 bytes .../datavisualization/volumetric/layer_magma.png | Bin 0 -> 17332 bytes .../datavisualization/volumetric/layer_water.png | Bin 0 -> 27124 bytes examples/datavisualization/volumetric/main.cpp | 10 +- .../datavisualization/volumetric/volumetric.cpp | 332 ++++++++++++++++++--- examples/datavisualization/volumetric/volumetric.h | 13 + .../datavisualization/volumetric/volumetric.pro | 2 + .../datavisualization/volumetric/volumetric.qrc | 7 + src/datavisualization/data/qcustom3dvolume.cpp | 2 +- tests/volumetrictest/volumetrictest.cpp | 6 +- 10 files changed, 319 insertions(+), 53 deletions(-) create mode 100644 examples/datavisualization/volumetric/layer_ground.png create mode 100644 examples/datavisualization/volumetric/layer_magma.png create mode 100644 examples/datavisualization/volumetric/layer_water.png create mode 100644 examples/datavisualization/volumetric/volumetric.qrc diff --git a/examples/datavisualization/volumetric/layer_ground.png b/examples/datavisualization/volumetric/layer_ground.png new file mode 100644 index 00000000..3f96a122 Binary files /dev/null and b/examples/datavisualization/volumetric/layer_ground.png differ diff --git a/examples/datavisualization/volumetric/layer_magma.png b/examples/datavisualization/volumetric/layer_magma.png new file mode 100644 index 00000000..01434d35 Binary files /dev/null and b/examples/datavisualization/volumetric/layer_magma.png differ diff --git a/examples/datavisualization/volumetric/layer_water.png b/examples/datavisualization/volumetric/layer_water.png new file mode 100644 index 00000000..4d57563e Binary files /dev/null and b/examples/datavisualization/volumetric/layer_water.png differ diff --git a/examples/datavisualization/volumetric/main.cpp b/examples/datavisualization/volumetric/main.cpp index 68895b17..0acffe10 100644 --- a/examples/datavisualization/volumetric/main.cpp +++ b/examples/datavisualization/volumetric/main.cpp @@ -49,7 +49,7 @@ int main(int argc, char **argv) hLayout->addLayout(vLayout); hLayout->addLayout(vLayout2); - widget->setWindowTitle(QStringLiteral("Volumetric Object Example")); + widget->setWindowTitle(QStringLiteral("Volumetric object example - 3D terrain")); QCheckBox *sliceXCheckBox = new QCheckBox(widget); sliceXCheckBox->setText(QStringLiteral("Slice volume on X axis")); @@ -135,6 +135,10 @@ int main(int argc, char **argv) preserveOpacityCheckBox->setText(QStringLiteral("Preserve opacity")); preserveOpacityCheckBox->setChecked(true); + QCheckBox *transparentGroundCheckBox = new QCheckBox(widget); + transparentGroundCheckBox->setText(QStringLiteral("Transparent ground")); + transparentGroundCheckBox->setChecked(false); + QCheckBox *useHighDefShaderCheckBox = new QCheckBox(widget); useHighDefShaderCheckBox->setText(QStringLiteral("Use HD shader")); useHighDefShaderCheckBox->setChecked(true); @@ -155,6 +159,7 @@ int main(int argc, char **argv) vLayout2->addWidget(alphaMultiplierLabel); vLayout2->addWidget(alphaMultiplierSlider); vLayout2->addWidget(preserveOpacityCheckBox); + vLayout2->addWidget(transparentGroundCheckBox); vLayout2->addWidget(useHighDefShaderCheckBox, 1, Qt::AlignTop); VolumetricModifier *modifier = new VolumetricModifier(graph); @@ -164,6 +169,7 @@ int main(int argc, char **argv) modifier->setSliceSliders(sliceXSlider, sliceYSlider, sliceZSlider); modifier->setSliceLabels(sliceImageXLabel, sliceImageYLabel, sliceImageZLabel); modifier->setAlphaMultiplierLabel(alphaMultiplierLabel); + modifier->setTransparentGround(transparentGroundCheckBox->isChecked()); QObject::connect(fpsCheckBox, &QCheckBox::stateChanged, modifier, &VolumetricModifier::setFpsMeasurement); @@ -189,6 +195,8 @@ int main(int argc, char **argv) &VolumetricModifier::changeColorTable); QObject::connect(preserveOpacityCheckBox, &QCheckBox::stateChanged, modifier, &VolumetricModifier::setPreserveOpacity); + QObject::connect(transparentGroundCheckBox, &QCheckBox::stateChanged, modifier, + &VolumetricModifier::setTransparentGround); QObject::connect(useHighDefShaderCheckBox, &QCheckBox::stateChanged, modifier, &VolumetricModifier::setUseHighDefShader); QObject::connect(alphaMultiplierSlider, &QSlider::valueChanged, modifier, diff --git a/examples/datavisualization/volumetric/volumetric.cpp b/examples/datavisualization/volumetric/volumetric.cpp index 1de4b1b7..156e0bf4 100644 --- a/examples/datavisualization/volumetric/volumetric.cpp +++ b/examples/datavisualization/volumetric/volumetric.cpp @@ -34,7 +34,21 @@ const int lowDetailSize(128); const int mediumDetailSize(256); const int highDetailSize(512); const int colorTableSize(256); -const int cutOffColorIndex(15); +const int layerDataSize(512); +const int mineShaftDiameter(1); + +const int airColorIndex(254); +const int mineShaftColorIndex(255); +const int layerColorThickness(60); +const int magmaColorsMin(0); +const int magmaColorsMax(layerColorThickness); +const int aboveWaterGroundColorsMin(magmaColorsMax + 1); +const int aboveWaterGroundColorsMax(aboveWaterGroundColorsMin + layerColorThickness); +const int underWaterGroundColorsMin(aboveWaterGroundColorsMax + 1); +const int underWaterGroundColorsMax(underWaterGroundColorsMin + layerColorThickness); +const int waterColorsMin(underWaterGroundColorsMax + 1); +const int waterColorsMax(waterColorsMin + layerColorThickness); +const int terrainTransparency(12); VolumetricModifier::VolumetricModifier(Q3DScatter *scatter) : m_graph(scatter), @@ -49,9 +63,12 @@ VolumetricModifier::VolumetricModifier(Q3DScatter *scatter) m_highDetailData(0), m_mediumDetailIndex(0), m_highDetailIndex(0), + m_mediumDetailShaftIndex(0), + m_highDetailShaftIndex(0), m_sliceSliderX(0), m_sliceSliderY(0), m_sliceSliderZ(0), + m_usingPrimaryTable(true), m_sliceLabelX(0), m_sliceLabelY(0), m_sliceLabelZ(0) @@ -66,7 +83,14 @@ VolumetricModifier::VolumetricModifier(Q3DScatter *scatter) m_mediumDetailData = new QVector(mediumDetailSize * mediumDetailSize * mediumDetailSize / 2); m_highDetailData = new QVector(highDetailSize * highDetailSize * highDetailSize / 2); + initHeightMap(QStringLiteral(":/heightmaps/layer_ground.png"), m_groundLayer); + initHeightMap(QStringLiteral(":/heightmaps/layer_water.png"), m_waterLayer); + initHeightMap(QStringLiteral(":/heightmaps/layer_magma.png"), m_magmaLayer); + + initMineShaftArray(); + createVolume(lowDetailSize, 0, lowDetailSize, m_lowDetailData); + excavateMineShaft(lowDetailSize, 0, m_mineShaftArray.size(), m_lowDetailData); m_volumeItem = new QCustom3DVolume; m_volumeItem->setScaling(QVector3D(2.0f, 1.0f, 2.0f)); @@ -77,37 +101,47 @@ VolumetricModifier::VolumetricModifier(Q3DScatter *scatter) m_volumeItem->setTextureData(new QVector(*m_lowDetailData)); // Generate color tables. - // Both tables have a fully transparent colors to fill outer portions of the volume. - - // The primary color table. - // The top two layers are transparent. m_colorTable1.resize(colorTableSize); m_colorTable2.resize(colorTableSize); - for (int i = 1; i < colorTableSize; i++) { - if (i < cutOffColorIndex) - m_colorTable1[i] = qRgba(0, 0, 0, 0); - else if (i < 60) - m_colorTable1[i] = qRgba((i * 2) + 120, 0, 0, 15); - else if (i < 120) - m_colorTable1[i] = qRgba(0, ((i - 60) * 2) + 120, 0, 50); - else if (i < 180) - m_colorTable1[i] = qRgba(0, 0, ((i - 120) * 2) + 120, 255); - else - m_colorTable1[i] = qRgba(i, i, i, 255); + for (int i = 0; i < colorTableSize - 2; i++) { + if (i < magmaColorsMax) { + m_colorTable1[i] = qRgba(130 - (i * 2), 0, 0, 255); + } else if (i < aboveWaterGroundColorsMax) { + m_colorTable1[i] = qRgba(0, ((i - magmaColorsMax) * 2) + 120, 0, terrainTransparency); + } else if (i < underWaterGroundColorsMax) { + m_colorTable1[i] = qRgba(((i - aboveWaterGroundColorsMax) * 2) + 30, + ((i - aboveWaterGroundColorsMax) * 2) + 100, + ((i - aboveWaterGroundColorsMax) * 2) + 30, terrainTransparency); + } else if (i < waterColorsMax) { + m_colorTable1[i] = qRgba(0, 0, ((i - underWaterGroundColorsMax) * 2) + 120, + terrainTransparency); + } else { + m_colorTable1[i] = qRgba(0, 0, 0, 0); // Not used + } } - - // The alternate color table. - // The first visible layer is a thin opaque color, and rest of the volume uses a smooth - // transparent gradient. - for (int i = 1; i < colorTableSize; i++) { - if (i < cutOffColorIndex) - m_colorTable2[i] = qRgba(0, 0, 0, 0); - else if (i < cutOffColorIndex + 4) - m_colorTable2[i] = qRgba(75, 150, 0, 255); - else - m_colorTable2[i] = qRgba(i, 0, 255 - i, i); + m_colorTable1[airColorIndex] = qRgba(0, 0, 0, 0); + m_colorTable1[mineShaftColorIndex] = qRgba(50, 50, 50, 255); + + // The alternate color table just has gray gradients for all terrain except water + for (int i = 0; i < colorTableSize - 2; i++) { + if (i < magmaColorsMax) { + m_colorTable2[i] = qRgba(((i - aboveWaterGroundColorsMax) * 2), + ((i - aboveWaterGroundColorsMax) * 2), + ((i - aboveWaterGroundColorsMax) * 2), 255); + } else if (i < underWaterGroundColorsMax) { + m_colorTable2[i] = qRgba(((i - aboveWaterGroundColorsMax) * 2), + ((i - aboveWaterGroundColorsMax) * 2), + ((i - aboveWaterGroundColorsMax) * 2), terrainTransparency); + } else if (i < waterColorsMax) { + m_colorTable2[i] = qRgba(0, 0, ((i - underWaterGroundColorsMax) * 2) + 120, + terrainTransparency); + } else { + m_colorTable2[i] = qRgba(0, 0, 0, 0); // Not used + } } + m_colorTable2[airColorIndex] = qRgba(0, 0, 0, 0); + m_colorTable2[mineShaftColorIndex] = qRgba(255, 255, 0, 255); m_volumeItem->setColorTable(m_colorTable1); @@ -246,18 +280,26 @@ void VolumetricModifier::handleFpsChange(qreal fps) void VolumetricModifier::handleTimeout() { - if (m_mediumDetailIndex < mediumDetailSize) { - m_mediumDetailIndex = createVolume(mediumDetailSize, m_mediumDetailIndex, 4, - m_mediumDetailData); - if (m_mediumDetailIndex == mediumDetailSize) { + if (!m_mediumDetailRB->isEnabled()) { + if (m_mediumDetailIndex != mediumDetailSize) { + m_mediumDetailIndex = createVolume(mediumDetailSize, m_mediumDetailIndex, 4, + m_mediumDetailData); + } else if (m_mediumDetailShaftIndex != m_mineShaftArray.size()) { + m_mediumDetailShaftIndex = excavateMineShaft(mediumDetailSize, m_mediumDetailShaftIndex, + 1, m_mediumDetailData ); + } else { m_mediumDetailRB->setEnabled(true); QString label = QStringLiteral("Medium (%1x%2x%1)"); m_mediumDetailRB->setText(label.arg(mediumDetailSize).arg(mediumDetailSize / 2)); } - } else if (m_highDetailIndex < highDetailSize) { - m_highDetailIndex = createVolume(highDetailSize, m_highDetailIndex, 1, - m_highDetailData); - if (m_highDetailIndex == highDetailSize) { + } else if (!m_highDetailRB->isEnabled()) { + if (m_highDetailIndex != highDetailSize) { + m_highDetailIndex = createVolume(highDetailSize, m_highDetailIndex, 1, + m_highDetailData); + } else if (m_highDetailShaftIndex != m_mineShaftArray.size()) { + m_highDetailShaftIndex = excavateMineShaft(highDetailSize, m_highDetailShaftIndex, 1, + m_highDetailData); + } else { m_highDetailRB->setEnabled(true); QString label = QStringLiteral("High (%1x%2x%1)"); m_highDetailRB->setText(label.arg(highDetailSize).arg(highDetailSize / 2)); @@ -313,6 +355,11 @@ void VolumetricModifier::setSliceSliders(QSlider *sliderX, QSlider *sliderY, QSl m_sliceSliderX = sliderX; m_sliceSliderY = sliderY; m_sliceSliderZ = sliderZ; + + // Set sliders to interesting values + m_sliceSliderX->setValue(715); + m_sliceSliderY->setValue(612); + m_sliceSliderZ->setValue(715); } void VolumetricModifier::changeColorTable(int enabled) @@ -322,6 +369,8 @@ void VolumetricModifier::changeColorTable(int enabled) else m_volumeItem->setColorTable(m_colorTable1); + m_usingPrimaryTable = !enabled; + // Rerender image labels adjustSliceX(m_sliceSliderX->value()); adjustSliceY(m_sliceSliderY->value()); @@ -338,6 +387,25 @@ void VolumetricModifier::setPreserveOpacity(bool enabled) adjustSliceZ(m_sliceSliderZ->value()); } +void VolumetricModifier::setTransparentGround(bool enabled) +{ + int newAlpha = enabled ? terrainTransparency : 255; + for (int i = aboveWaterGroundColorsMin; i < underWaterGroundColorsMax; i++) { + QRgb oldColor1 = m_colorTable1.at(i); + QRgb oldColor2 = m_colorTable2.at(i); + m_colorTable1[i] = qRgba(qRed(oldColor1), qGreen(oldColor1), qBlue(oldColor1), newAlpha); + m_colorTable2[i] = qRgba(qRed(oldColor2), qGreen(oldColor2), qBlue(oldColor2), newAlpha); + } + if (m_usingPrimaryTable) + m_volumeItem->setColorTable(m_colorTable1); + else + m_volumeItem->setColorTable(m_colorTable2); + + adjustSliceX(m_sliceSliderX->value()); + adjustSliceY(m_sliceSliderY->value()); + adjustSliceZ(m_sliceSliderZ->value()); +} + void VolumetricModifier::setUseHighDefShader(bool enabled) { m_volumeItem->setUseHighDefShader(enabled); @@ -361,29 +429,65 @@ void VolumetricModifier::adjustAlphaMultiplier(int value) adjustSliceZ(m_sliceSliderZ->value()); } +void VolumetricModifier::initHeightMap(QString fileName, QVector &layerData) +{ + QImage heightImage(fileName); + + layerData.resize(layerDataSize * layerDataSize); + const uchar *bits = heightImage.bits(); + int index = 0; + QVector colorTable = heightImage.colorTable(); + for (int i = 0; i < layerDataSize; i++) { + for (int j = 0; j < layerDataSize; j++) { + layerData[index] = qRed(colorTable.at(bits[index])); + index++; + } + } +} + int VolumetricModifier::createVolume(int textureSize, int startIndex, int count, QVector *textureData) { - // Generate example texture data for an half-ellipsoid with a section missing. - // This can take a while if the dimensions are large, so we support incremental data generation. - - QVector3D midPoint(float(textureSize) / 2.0f, - float(textureSize) / 2.0f, - float(textureSize) / 2.0f); - + // Generate volume from layer data. int index = startIndex * textureSize * textureSize / 2.0f; int endIndex = startIndex + count; if (endIndex > textureSize) endIndex = textureSize; + QVector magmaHeights(textureSize); + QVector waterHeights(textureSize); + QVector groundHeights(textureSize); + float multiplier = float(layerDataSize) / float(textureSize); for (int i = startIndex; i < endIndex; i++) { + // Generate layer height arrays + for (int l = 0; l < textureSize; l++) { + int layerIndex = (int(i * multiplier) * layerDataSize + int(l * multiplier)); + magmaHeights[l] = int(m_magmaLayer.at(layerIndex)); + waterHeights[l] = int(m_waterLayer.at(layerIndex)); + groundHeights[l] = int(m_groundLayer.at(layerIndex)); + } for (int j = 0; j < textureSize / 2; j++) { for (int k = 0; k < textureSize; k++) { - int colorIndex = 0; - // Take a section out of the ellipsoid - if (i >= textureSize / 2 || j >= textureSize / 4 || k >= textureSize / 2) { - QVector3D distVec = QVector3D(float(k), float(j * 2), float(i)) - midPoint; - float adjLen = qMin(255.0f, (distVec.length() * 512.0f / float(textureSize))); - colorIndex = 255 - int(adjLen); + int colorIndex; + int height((layerDataSize - (j * 2 * multiplier)) / 2); + if (height < magmaHeights.at(k)) { + // Magma layer + colorIndex = int((float(height) / colorTableSize) + * float(layerColorThickness)) + magmaColorsMin; + } else if (height <= groundHeights.at(k) && height <= waterHeights.at(k)) { + // Ground layer below water + colorIndex = int((float(waterHeights.at(k) - height) / colorTableSize) + * float(layerColorThickness)) + underWaterGroundColorsMin; + } else if (height <= waterHeights.at(k)) { + // Water layer where water goes over ground + colorIndex = int((float(height - magmaHeights.at(k)) / colorTableSize) + * float(layerColorThickness)) + waterColorsMin; + } else if (height <= groundHeights.at(k)) { + // Ground above water + colorIndex = int((float(height - waterHeights.at(k)) / colorTableSize) + * float(layerColorThickness)) + aboveWaterGroundColorsMin; + } else { + // Rest is air + colorIndex = airColorIndex; } (*textureData)[index] = colorIndex; @@ -391,7 +495,139 @@ int VolumetricModifier::createVolume(int textureSize, int startIndex, int count, } } } + return endIndex; +} + +int VolumetricModifier::excavateMineShaft(int textureSize, int startIndex, int count, + QVector *textureData) +{ + int endIndex = startIndex + count; + if (endIndex > m_mineShaftArray.size()) + endIndex = m_mineShaftArray.size(); + int shaftSize = mineShaftDiameter * textureSize / lowDetailSize; + for (int i = startIndex; i < endIndex; i++) { + QVector3D shaftStart(m_mineShaftArray.at(i).first); + QVector3D shaftEnd(m_mineShaftArray.at(i).second); + int shaftLen = (shaftEnd - shaftStart).length() * lowDetailSize; + int dataX = shaftStart.x() * textureSize - (shaftSize / 2); + int dataY = (shaftStart.y() * textureSize - (shaftSize / 2)) / 2; + int dataZ = shaftStart.z() * textureSize - (shaftSize / 2); + int dataIndex = dataX + (dataY * textureSize) + dataZ * (textureSize * textureSize / 2); + if (shaftStart.x() != shaftEnd.x()) { + for (int j = 0; j <= shaftLen; j++) { + excavateMineBlock(textureSize, dataIndex, shaftSize, textureData); + dataIndex += shaftSize; + } + } else if (shaftStart.y() != shaftEnd.y()) { + shaftLen /= 2; // Vertical shafts are half as long + for (int j = 0; j <= shaftLen; j++) { + excavateMineBlock(textureSize, dataIndex, shaftSize, textureData); + dataIndex += textureSize * shaftSize; + } + } else { + for (int j = 0; j <= shaftLen; j++) { + excavateMineBlock(textureSize, dataIndex, shaftSize, textureData); + dataIndex += (textureSize * textureSize / 2) * shaftSize; + } + } + + } return endIndex; } +void VolumetricModifier::excavateMineBlock(int textureSize, int dataIndex, int size, + QVector *textureData) +{ + for (int k = 0; k < size; k++) { + int curIndex = dataIndex + (k * textureSize * textureSize / 2); + for (int l = 0; l < size; l++) { + curIndex = dataIndex + (k * textureSize * textureSize / 2) + + (l * textureSize); + for (int m = 0; m < size; m++) { + if (textureData->at(curIndex) != airColorIndex) + (*textureData)[curIndex] = mineShaftColorIndex; + curIndex++; + } + + } + } +} + +void VolumetricModifier::initMineShaftArray() +{ + m_mineShaftArray << QPair(QVector3D(0.7f, 0.1f, 0.7f), + QVector3D(0.7f, 0.8f, 0.7f)); + m_mineShaftArray << QPair(QVector3D(0.7f, 0.7f, 0.5f), + QVector3D(0.7f, 0.7f, 0.7f)); + + m_mineShaftArray << QPair(QVector3D(0.4f, 0.7f, 0.7f), + QVector3D(0.7f, 0.7f, 0.7f)); + m_mineShaftArray << QPair(QVector3D(0.4f, 0.7f, 0.7f), + QVector3D(0.4f, 0.7f, 0.8f)); + m_mineShaftArray << QPair(QVector3D(0.45f, 0.7f, 0.7f), + QVector3D(0.45f, 0.7f, 0.8f)); + m_mineShaftArray << QPair(QVector3D(0.5f, 0.7f, 0.7f), + QVector3D(0.5f, 0.7f, 0.8f)); + m_mineShaftArray << QPair(QVector3D(0.55f, 0.7f, 0.7f), + QVector3D(0.55f, 0.7f, 0.8f)); + m_mineShaftArray << QPair(QVector3D(0.6f, 0.7f, 0.7f), + QVector3D(0.6f, 0.7f, 0.8f)); + m_mineShaftArray << QPair(QVector3D(0.65f, 0.7f, 0.7f), + QVector3D(0.65f, 0.7f, 0.8f)); + + m_mineShaftArray << QPair(QVector3D(0.5f, 0.6f, 0.7f), + QVector3D(0.7f, 0.6f, 0.7f)); + m_mineShaftArray << QPair(QVector3D(0.5f, 0.6f, 0.7f), + QVector3D(0.5f, 0.6f, 0.8f)); + m_mineShaftArray << QPair(QVector3D(0.55f, 0.6f, 0.7f), + QVector3D(0.55f, 0.6f, 0.8f)); + m_mineShaftArray << QPair(QVector3D(0.6f, 0.6f, 0.7f), + QVector3D(0.6f, 0.6f, 0.8f)); + m_mineShaftArray << QPair(QVector3D(0.65f, 0.6f, 0.7f), + QVector3D(0.65f, 0.6f, 0.8f)); + + m_mineShaftArray << QPair(QVector3D(0.7f, 0.6f, 0.4f), + QVector3D(0.7f, 0.6f, 0.7f)); + m_mineShaftArray << QPair(QVector3D(0.6f, 0.6f, 0.45f), + QVector3D(0.8f, 0.6f, 0.45f)); + m_mineShaftArray << QPair(QVector3D(0.6f, 0.6f, 0.5f), + QVector3D(0.8f, 0.6f, 0.5f)); + m_mineShaftArray << QPair(QVector3D(0.6f, 0.6f, 0.55f), + QVector3D(0.8f, 0.6f, 0.55f)); + m_mineShaftArray << QPair(QVector3D(0.6f, 0.6f, 0.6f), + QVector3D(0.8f, 0.6f, 0.6f)); + m_mineShaftArray << QPair(QVector3D(0.6f, 0.6f, 0.65f), + QVector3D(0.8f, 0.6f, 0.65f)); + m_mineShaftArray << QPair(QVector3D(0.6f, 0.6f, 0.7f), + QVector3D(0.8f, 0.6f, 0.7f)); + + m_mineShaftArray << QPair(QVector3D(0.7f, 0.7f, 0.4f), + QVector3D(0.7f, 0.7f, 0.7f)); + m_mineShaftArray << QPair(QVector3D(0.6f, 0.7f, 0.45f), + QVector3D(0.8f, 0.7f, 0.45f)); + m_mineShaftArray << QPair(QVector3D(0.6f, 0.7f, 0.5f), + QVector3D(0.8f, 0.7f, 0.5f)); + m_mineShaftArray << QPair(QVector3D(0.6f, 0.7f, 0.55f), + QVector3D(0.8f, 0.7f, 0.55f)); + m_mineShaftArray << QPair(QVector3D(0.6f, 0.7f, 0.6f), + QVector3D(0.8f, 0.7f, 0.6f)); + m_mineShaftArray << QPair(QVector3D(0.6f, 0.7f, 0.65f), + QVector3D(0.8f, 0.7f, 0.65f)); + m_mineShaftArray << QPair(QVector3D(0.6f, 0.7f, 0.7f), + QVector3D(0.8f, 0.7f, 0.7f)); + + m_mineShaftArray << QPair(QVector3D(0.7f, 0.8f, 0.5f), + QVector3D(0.7f, 0.8f, 0.7f)); + m_mineShaftArray << QPair(QVector3D(0.6f, 0.8f, 0.55f), + QVector3D(0.8f, 0.8f, 0.55f)); + m_mineShaftArray << QPair(QVector3D(0.6f, 0.8f, 0.6f), + QVector3D(0.8f, 0.8f, 0.6f)); + m_mineShaftArray << QPair(QVector3D(0.6f, 0.8f, 0.65f), + QVector3D(0.8f, 0.8f, 0.65f)); + m_mineShaftArray << QPair(QVector3D(0.6f, 0.8f, 0.7f), + QVector3D(0.8f, 0.8f, 0.7f)); + + m_mineShaftArray << QPair(QVector3D(0.7f, 0.1f, 0.4f), + QVector3D(0.7f, 0.7f, 0.4f)); +} diff --git a/examples/datavisualization/volumetric/volumetric.h b/examples/datavisualization/volumetric/volumetric.h index 83af4b3c..27b053e2 100644 --- a/examples/datavisualization/volumetric/volumetric.h +++ b/examples/datavisualization/volumetric/volumetric.h @@ -60,13 +60,19 @@ public slots: void setSliceSliders(QSlider *sliderX, QSlider *sliderY, QSlider *sliderZ); void changeColorTable(int enabled); void setPreserveOpacity(bool enabled); + void setTransparentGround(bool enabled); void setUseHighDefShader(bool enabled); void adjustAlphaMultiplier(int value); private: + void initHeightMap(QString fileName, QVector &layerData); + void initMineShaftArray(); int createVolume(int textureSize, int startIndex, int count, QVector *textureData); + int excavateMineShaft(int textureSize, int startIndex, int count, + QVector *textureData); + void excavateMineBlock(int textureSize, int dataIndex, int size, QVector *textureData); Q3DScatter *m_graph; QCustom3DVolume *m_volumeItem; @@ -82,15 +88,22 @@ private: QTimer m_timer; int m_mediumDetailIndex; int m_highDetailIndex; + int m_mediumDetailShaftIndex; + int m_highDetailShaftIndex; QSlider *m_sliceSliderX; QSlider *m_sliceSliderY; QSlider *m_sliceSliderZ; QVector m_colorTable1; QVector m_colorTable2; + bool m_usingPrimaryTable; QLabel *m_sliceLabelX; QLabel *m_sliceLabelY; QLabel *m_sliceLabelZ; QLabel *m_alphaMultiplierLabel; + QVector m_magmaLayer; + QVector m_waterLayer; + QVector m_groundLayer; + QVector > m_mineShaftArray; }; #endif diff --git a/examples/datavisualization/volumetric/volumetric.pro b/examples/datavisualization/volumetric/volumetric.pro index c5e31891..fa355692 100644 --- a/examples/datavisualization/volumetric/volumetric.pro +++ b/examples/datavisualization/volumetric/volumetric.pro @@ -13,3 +13,5 @@ QT += widgets OTHER_FILES += doc/src/* \ doc/images/* + +RESOURCES += volumetric.qrc diff --git a/examples/datavisualization/volumetric/volumetric.qrc b/examples/datavisualization/volumetric/volumetric.qrc new file mode 100644 index 00000000..920fd1d2 --- /dev/null +++ b/examples/datavisualization/volumetric/volumetric.qrc @@ -0,0 +1,7 @@ + + + layer_ground.png + layer_magma.png + layer_water.png + + diff --git a/src/datavisualization/data/qcustom3dvolume.cpp b/src/datavisualization/data/qcustom3dvolume.cpp index 789b7718..ec5129ff 100644 --- a/src/datavisualization/data/qcustom3dvolume.cpp +++ b/src/datavisualization/data/qcustom3dvolume.cpp @@ -869,7 +869,7 @@ QImage QCustom3DVolumePrivate::renderSlice(Qt::Axis axis, int index) } } } else if (axis == Qt::YAxis) { - for (int i = 0; i < y; i++) { + for (int i = y - 1; i >= 0; i--) { const uchar *p = m_textureData->constData() + (index * dataWidth) + (frameSize * i); for (int j = 0; j < (x * pixelWidth); j++) { diff --git a/tests/volumetrictest/volumetrictest.cpp b/tests/volumetrictest/volumetrictest.cpp index 9c89e761..236273d7 100644 --- a/tests/volumetrictest/volumetrictest.cpp +++ b/tests/volumetrictest/volumetrictest.cpp @@ -117,7 +117,7 @@ void VolumetricModifier::adjustSliceX(int value) m_volumeItem2->setSliceIndexX(m_sliceIndexX); } m_sliceLabelX->setPixmap(QPixmap::fromImage( - m_volumeItem2->renderSlice(Qt::XAxis, m_sliceIndexX))); + m_volumeItem->renderSlice(Qt::XAxis, m_sliceIndexX))); } @@ -132,7 +132,7 @@ void VolumetricModifier::adjustSliceY(int value) m_volumeItem2->setSliceIndexY(m_sliceIndexY); } m_sliceLabelY->setPixmap(QPixmap::fromImage( - m_volumeItem2->renderSlice(Qt::YAxis, m_sliceIndexY))); + m_volumeItem->renderSlice(Qt::YAxis, m_sliceIndexY))); } void VolumetricModifier::adjustSliceZ(int value) @@ -146,7 +146,7 @@ void VolumetricModifier::adjustSliceZ(int value) m_volumeItem2->setSliceIndexZ(m_sliceIndexZ); } m_sliceLabelZ->setPixmap(QPixmap::fromImage( - m_volumeItem2->renderSlice(Qt::ZAxis, m_sliceIndexZ))); + m_volumeItem->renderSlice(Qt::ZAxis, m_sliceIndexZ))); } void VolumetricModifier::handleFpsChange() -- cgit v1.2.3 From d85e665b114692fcac7c8e16217c7148907584b0 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Tue, 2 Sep 2014 16:15:08 +0300 Subject: QML support for volume items MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I4061bfd4d288aabaea76847c9f692e71ecf5f94c Reviewed-by: Tomi Korpipää --- .../datavisualizationqml2_plugin.cpp | 1 + .../datavisualizationqml2_plugin.h | 2 + tests/qmlvolume/datasource.cpp | 90 ++++++++++++ tests/qmlvolume/datasource.h | 38 +++++ tests/qmlvolume/main.cpp | 56 ++++++++ tests/qmlvolume/qml/qmlvolume/NewButton.qml | 52 +++++++ tests/qmlvolume/qml/qmlvolume/main.qml | 159 +++++++++++++++++++++ tests/qmlvolume/qmlvolume.pro | 16 +++ tests/qmlvolume/qmlvolume.qrc | 6 + tests/tests.pro | 3 +- 10 files changed, 422 insertions(+), 1 deletion(-) create mode 100644 tests/qmlvolume/datasource.cpp create mode 100644 tests/qmlvolume/datasource.h create mode 100644 tests/qmlvolume/main.cpp create mode 100644 tests/qmlvolume/qml/qmlvolume/NewButton.qml create mode 100644 tests/qmlvolume/qml/qmlvolume/main.qml create mode 100644 tests/qmlvolume/qmlvolume.pro create mode 100644 tests/qmlvolume/qmlvolume.qrc diff --git a/src/datavisualizationqml2/datavisualizationqml2_plugin.cpp b/src/datavisualizationqml2/datavisualizationqml2_plugin.cpp index f5e2b4e0..c0bd4183 100644 --- a/src/datavisualizationqml2/datavisualizationqml2_plugin.cpp +++ b/src/datavisualizationqml2/datavisualizationqml2_plugin.cpp @@ -119,6 +119,7 @@ void QtDataVisualizationQml2Plugin::registerTypes(const char *uri) // New types qmlRegisterType(uri, 1, 2, "InputHandler3D"); qmlRegisterType(uri, 1, 2, "TouchInputHandler3D"); + qmlRegisterType(uri, 1, 2, "Custom3DVolume"); } QT_END_NAMESPACE_DATAVISUALIZATION diff --git a/src/datavisualizationqml2/datavisualizationqml2_plugin.h b/src/datavisualizationqml2/datavisualizationqml2_plugin.h index 8d2be659..8ece1c15 100644 --- a/src/datavisualizationqml2/datavisualizationqml2_plugin.h +++ b/src/datavisualizationqml2/datavisualizationqml2_plugin.h @@ -48,6 +48,7 @@ #include "declarativescene_p.h" #include "qcustom3ditem.h" #include "qcustom3dlabel.h" +#include "qcustom3dvolume.h" #include @@ -103,6 +104,7 @@ QML_DECLARE_TYPE(QTouch3DInputHandler) QML_DECLARE_TYPE(QCustom3DItem) QML_DECLARE_TYPE(QCustom3DLabel) +QML_DECLARE_TYPE(QCustom3DVolume) QT_BEGIN_NAMESPACE_DATAVISUALIZATION diff --git a/tests/qmlvolume/datasource.cpp b/tests/qmlvolume/datasource.cpp new file mode 100644 index 00000000..e1fe5385 --- /dev/null +++ b/tests/qmlvolume/datasource.cpp @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +#include "datasource.h" +#include +#include +#include + +using namespace QtDataVisualization; + +Q_DECLARE_METATYPE(QCustom3DVolume *) + +DataSource::DataSource(QObject *parent) : + QObject(parent) +{ + qRegisterMetaType(); +} + +DataSource::~DataSource() +{ +} + +void DataSource::fillVolume(QCustom3DVolume *volumeItem) +{ + // Generate example texture data for an half-ellipsoid with a section missing. + // This can take a while if the dimensions are large, so we support incremental data generation. + + int index = 0; + int textureSize = 256; + QVector3D midPoint(float(textureSize) / 2.0f, + float(textureSize) / 2.0f, + float(textureSize) / 2.0f); + + QVector *textureData = new QVector(textureSize * textureSize * textureSize / 2); + for (int i = 0; i < textureSize; i++) { + for (int j = 0; j < textureSize / 2; j++) { + for (int k = 0; k < textureSize; k++) { + int colorIndex = 0; + // Take a section out of the ellipsoid + if (i >= textureSize / 2 || j >= textureSize / 4 || k >= textureSize / 2) { + QVector3D distVec = QVector3D(float(k), float(j * 2), float(i)) - midPoint; + float adjLen = qMin(255.0f, (distVec.length() * 512.0f / float(textureSize))); + colorIndex = 255 - int(adjLen); + } + + (*textureData)[index] = colorIndex; + index++; + } + } + } + + volumeItem->setScaling(QVector3D(2.0f, 1.0f, 2.0f)); + volumeItem->setTextureWidth(textureSize); + volumeItem->setTextureHeight(textureSize / 2); + volumeItem->setTextureDepth(textureSize); + volumeItem->setTextureFormat(QImage::Format_Indexed8); + volumeItem->setTextureData(textureData); + + QVector colorTable(256); + + for (int i = 1; i < 256; i++) { + if (i < 15) + colorTable[i] = qRgba(0, 0, 0, 0); + else if (i < 60) + colorTable[i] = qRgba((i * 2) + 120, 0, 0, 15); + else if (i < 120) + colorTable[i] = qRgba(0, ((i - 60) * 2) + 120, 0, 50); + else if (i < 180) + colorTable[i] = qRgba(0, 0, ((i - 120) * 2) + 120, 255); + else + colorTable[i] = qRgba(i, i, i, 255); + } + + volumeItem->setColorTable(colorTable); +} diff --git a/tests/qmlvolume/datasource.h b/tests/qmlvolume/datasource.h new file mode 100644 index 00000000..fc543792 --- /dev/null +++ b/tests/qmlvolume/datasource.h @@ -0,0 +1,38 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +#ifndef DATASOURCE_H +#define DATASOURCE_H + +#include +#include + +using namespace QtDataVisualization; + +class DataSource : public QObject +{ + Q_OBJECT +public: + explicit DataSource(QObject *parent = 0); + virtual ~DataSource(); + +public: + Q_INVOKABLE void fillVolume(QCustom3DVolume *volumeItem); +}; + +#endif diff --git a/tests/qmlvolume/main.cpp b/tests/qmlvolume/main.cpp new file mode 100644 index 00000000..85de7eed --- /dev/null +++ b/tests/qmlvolume/main.cpp @@ -0,0 +1,56 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +#include "datasource.h" + +#include + +#include +#include +#include +#include +#include + +int main(int argc, char *argv[]) +{ + QGuiApplication app(argc, argv); + + QQuickView viewer; + + // The following are needed to make examples run without having to install the module + // in desktop environments. +#ifdef Q_OS_WIN + QString extraImportPath(QStringLiteral("%1/../../../%2")); +#else + QString extraImportPath(QStringLiteral("%1/../../%2")); +#endif + viewer.engine()->addImportPath(extraImportPath.arg(QGuiApplication::applicationDirPath(), + QString::fromLatin1("qml"))); + QObject::connect(viewer.engine(), &QQmlEngine::quit, &viewer, &QWindow::close); + + viewer.setTitle(QStringLiteral("QML volume example")); + + DataSource dataSource; + viewer.rootContext()->setContextProperty("dataSource", &dataSource); + + viewer.setSource(QUrl("qrc:/qml/qmlvolume/main.qml")); + viewer.setResizeMode(QQuickView::SizeRootObjectToView); + viewer.show(); + + return app.exec(); +} diff --git a/tests/qmlvolume/qml/qmlvolume/NewButton.qml b/tests/qmlvolume/qml/qmlvolume/NewButton.qml new file mode 100644 index 00000000..e4fb99d2 --- /dev/null +++ b/tests/qmlvolume/qml/qmlvolume/NewButton.qml @@ -0,0 +1,52 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +import QtQuick 2.1 +import QtQuick.Controls 1.0 +import QtQuick.Controls.Styles 1.0 + +Item { + id: newbutton + + property alias text: buttonText.text + + signal clicked + + implicitWidth: buttonText.implicitWidth + 5 + implicitHeight: buttonText.implicitHeight + 10 + + Button { + id: buttonText + width: parent.width + height: parent.height + + style: ButtonStyle { + label: Component { + Text { + text: buttonText.text + clip: true + wrapMode: Text.WordWrap + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + anchors.fill: parent + } + } + } + onClicked: newbutton.clicked() + } +} diff --git a/tests/qmlvolume/qml/qmlvolume/main.qml b/tests/qmlvolume/qml/qmlvolume/main.qml new file mode 100644 index 00000000..7af94b21 --- /dev/null +++ b/tests/qmlvolume/qml/qmlvolume/main.qml @@ -0,0 +1,159 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +import QtQuick 2.1 +import QtQuick.Layouts 1.0 +import QtQuick.Controls 1.0 +import QtDataVisualization 1.2 +import "." + +Item { + id: mainView + width: 1280 + height: 1024 + + Item { + id: dataView + anchors.bottom: parent.bottom + width: parent.width + height: parent.height - buttonLayout.height + + Surface3D { + id: surfaceGraph + + width: dataView.width + height: dataView.height + orthoProjection: true + measureFps: true + + onCurrentFpsChanged: { + if (fps > 10) + fpsText.text = "FPS: " + Math.round(surfaceGraph.currentFps) + else + fpsText.text = "FPS: " + Math.round(surfaceGraph.currentFps * 10.0) / 10.0 + } + + Surface3DSeries { + id: surfaceSeries + drawMode: Surface3DSeries.DrawSurface; + flatShadingEnabled: false; + meshSmooth: true + itemLabelFormat: "@xLabel, @zLabel: @yLabel" + itemLabelVisible: false + + onItemLabelChanged: { + if (surfaceSeries.selectedPoint === surfaceSeries.invalidSelectionPosition) + selectionText.text = "No selection" + else + selectionText.text = surfaceSeries.itemLabel + } + } + + Component.onCompleted: { + mainView.createVolume(); + } + } + } + + Rectangle { + width: parent.width + height: 50 + anchors.left: parent.left + anchors.top: parent.top + color: surfaceGraph.theme.backgroundColor + + ColumnLayout { + anchors.fill: parent + RowLayout { + id: sliderLayout + anchors.top: parent.top + Layout.fillHeight: true + Layout.fillWidth: true + Layout.minimumHeight: 150 + spacing: 0 + + Rectangle { + Layout.fillHeight: true + Layout.fillWidth: true + Layout.minimumWidth: fpsText.implicitWidth + 10 + Layout.maximumWidth: fpsText.implicitWidth + 10 + Layout.minimumHeight: 50 + Layout.maximumHeight: 50 + Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter + + border.color: "gray" + border.width: 1 + radius: 4 + + Text { + id: fpsText + anchors.fill: parent + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + } + } + } + + RowLayout { + id: buttonLayout + Layout.fillHeight: true + Layout.fillWidth: true + Layout.minimumHeight: 50 + anchors.bottom: parent.bottom + spacing: 0 + + NewButton { + id: sliceButton + Layout.fillHeight: true + Layout.fillWidth: true + + text: "Slice" + + onClicked: { + if (volumeItem.sliceIndexZ == -1) + volumeItem.sliceIndexZ = 128 + else + volumeItem.sliceIndexZ = -1 + } + } + NewButton { + id: exitButton + Layout.fillHeight: true + Layout.fillWidth: true + + text: "Quit" + + onClicked: Qt.quit(0); + } + } + } + + } + + Custom3DVolume { + id: volumeItem + alphaMultiplier: 0.3 + preserveOpacity: true + useHighDefShader: false + } + + function createVolume() { + surfaceGraph.addCustomItem(volumeItem) + dataSource.fillVolume(volumeItem) + } +} diff --git a/tests/qmlvolume/qmlvolume.pro b/tests/qmlvolume/qmlvolume.pro new file mode 100644 index 00000000..1d58b668 --- /dev/null +++ b/tests/qmlvolume/qmlvolume.pro @@ -0,0 +1,16 @@ +!include( ../tests.pri ) { + error( "Couldn't find the tests.pri file!" ) +} + +QT += datavisualization + +# The .cpp file which was generated for your project. Feel free to hack it. +SOURCES += main.cpp \ + datasource.cpp +HEADERS += datasource.h + +RESOURCES += qmlvolume.qrc + +OTHER_FILES += doc/src/* \ + doc/images/* \ + qml/qmlvolume/* diff --git a/tests/qmlvolume/qmlvolume.qrc b/tests/qmlvolume/qmlvolume.qrc new file mode 100644 index 00000000..18fe57e1 --- /dev/null +++ b/tests/qmlvolume/qmlvolume.qrc @@ -0,0 +1,6 @@ + + + qml/qmlvolume/main.qml + qml/qmlvolume/NewButton.qml + + diff --git a/tests/tests.pro b/tests/tests.pro index 21036b59..9073ca9f 100644 --- a/tests/tests.pro +++ b/tests/tests.pro @@ -17,7 +17,8 @@ SUBDIRS += barstest \ qmlmultiwindow \ itemmodeltest \ qmlmultitest \ - volumetrictest + volumetrictest \ + qmlvolume #SUBDIRS += kinectsurface -- cgit v1.2.3 From ac96d7ae26fff464a5ef9ab327fe46499f0077bf Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Wed, 3 Sep 2014 09:28:44 +0300 Subject: Make volume items draw after regular custom items MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since volume items typically contain transparencies, make them draw after regular custom items, which are less likely to be transparent. Change-Id: Id7c48b6c77d7ed8654b72923d7dccf4158c9c088 Reviewed-by: Tomi Korpipää --- .../engine/abstract3drenderer.cpp | 326 +++++++++++---------- tests/volumetrictest/volumetrictest.cpp | 13 +- 2 files changed, 183 insertions(+), 156 deletions(-) diff --git a/src/datavisualization/engine/abstract3drenderer.cpp b/src/datavisualization/engine/abstract3drenderer.cpp index f031a34c..640502ad 100644 --- a/src/datavisualization/engine/abstract3drenderer.cpp +++ b/src/datavisualization/engine/abstract3drenderer.cpp @@ -1193,187 +1193,203 @@ void Abstract3DRenderer::drawCustomItems(RenderingState state, shader->setUniformValue(shader->view(), viewMatrix); } - // Draw custom items - foreach (CustomRenderItem *item, m_customRenderCache) { - // Check that the render item is visible, and skip drawing if not - if (!item->isVisible()) - continue; - - // Check if the render item is in data coordinates and not within axis ranges, and skip drawing if it is - if (!item->isPositionAbsolute() - && (item->position().x() < m_axisCacheX.min() - || item->position().x() > m_axisCacheX.max() - || item->position().z() < m_axisCacheZ.min() - || item->position().z() > m_axisCacheZ.max() - || item->position().y() < m_axisCacheY.min() - || item->position().y() > m_axisCacheY.max())) { - continue; - } - - QMatrix4x4 modelMatrix; - QMatrix4x4 itModelMatrix; - QMatrix4x4 MVPMatrix; - - QQuaternion rotation = item->rotation(); - // Check if the (label) item should be facing camera, and adjust rotation accordingly - if (item->isFacingCamera()) { - float camRotationX = m_cachedScene->activeCamera()->xRotation(); - float camRotationY = m_cachedScene->activeCamera()->yRotation(); - rotation = QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, -camRotationX) - * QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, -camRotationY); - } - - if (m_reflectionEnabled) { - if (reflection < 0.0f) { - if (item->itemPointer()->d_ptr->m_isLabelItem) + // Draw custom items - first regular and then volumes + bool volumeDetected = false; + int loopCount = 0; + while (loopCount < 2) { + foreach (CustomRenderItem *item, m_customRenderCache) { + // Check that the render item is visible, and skip drawing if not + if (!item->isVisible()) + continue; + if (loopCount == 0) { + if (item->isVolume()) { + volumeDetected = true; continue; - else - glCullFace(GL_FRONT); + } } else { - glCullFace(GL_BACK); + if (!item->isVolume()) + continue; + } + + // Check if the render item is in data coordinates and not within axis ranges, and skip drawing if it is + if (!item->isPositionAbsolute() + && (item->position().x() < m_axisCacheX.min() + || item->position().x() > m_axisCacheX.max() + || item->position().z() < m_axisCacheZ.min() + || item->position().z() > m_axisCacheZ.max() + || item->position().y() < m_axisCacheY.min() + || item->position().y() > m_axisCacheY.max())) { + continue; } - QVector3D trans = item->translation(); - trans.setY(reflection * trans.y()); - modelMatrix.translate(trans); - if (reflection < 0.0f) { - QQuaternion mirror = QQuaternion(rotation.scalar(), - -rotation.x(), rotation.y(), -rotation.z()); - modelMatrix.rotate(mirror); - itModelMatrix.rotate(mirror); + + QMatrix4x4 modelMatrix; + QMatrix4x4 itModelMatrix; + QMatrix4x4 MVPMatrix; + + QQuaternion rotation = item->rotation(); + // Check if the (label) item should be facing camera, and adjust rotation accordingly + if (item->isFacingCamera()) { + float camRotationX = m_cachedScene->activeCamera()->xRotation(); + float camRotationY = m_cachedScene->activeCamera()->yRotation(); + rotation = QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, -camRotationX) + * QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, -camRotationY); + } + + if (m_reflectionEnabled) { + if (reflection < 0.0f) { + if (item->itemPointer()->d_ptr->m_isLabelItem) + continue; + else + glCullFace(GL_FRONT); + } else { + glCullFace(GL_BACK); + } + QVector3D trans = item->translation(); + trans.setY(reflection * trans.y()); + modelMatrix.translate(trans); + if (reflection < 0.0f) { + QQuaternion mirror = QQuaternion(rotation.scalar(), + -rotation.x(), rotation.y(), -rotation.z()); + modelMatrix.rotate(mirror); + itModelMatrix.rotate(mirror); + } else { + modelMatrix.rotate(rotation); + itModelMatrix.rotate(rotation); + } + QVector3D scale = item->scaling(); + scale.setY(reflection * scale.y()); + modelMatrix.scale(scale); } else { + modelMatrix.translate(item->translation()); modelMatrix.rotate(rotation); + modelMatrix.scale(item->scaling()); itModelMatrix.rotate(rotation); } - QVector3D scale = item->scaling(); - scale.setY(reflection * scale.y()); - modelMatrix.scale(scale); - } else { - modelMatrix.translate(item->translation()); - modelMatrix.rotate(rotation); - modelMatrix.scale(item->scaling()); - itModelMatrix.rotate(rotation); - } - if (!item->isFacingCamera()) - itModelMatrix.scale(item->scaling()); - MVPMatrix = projectionViewMatrix * modelMatrix; + if (!item->isFacingCamera()) + itModelMatrix.scale(item->scaling()); + MVPMatrix = projectionViewMatrix * modelMatrix; - if (RenderingNormal == state) { - // Normal render + if (RenderingNormal == state) { + // Normal render #if !defined(QT_OPENGL_ES_2) - ShaderHelper *prevShader = shader; - if (item->isVolume()) { - if (item->sliceIndexX() >= 0 - || item->sliceIndexY() >= 0 - || item->sliceIndexZ() >= 0) { - shader = m_volumeTextureSliceShader; - } else if (item->useHighDefShader()) { - shader = m_volumeTextureShader; + ShaderHelper *prevShader = shader; + if (item->isVolume()) { + if (item->sliceIndexX() >= 0 + || item->sliceIndexY() >= 0 + || item->sliceIndexZ() >= 0) { + shader = m_volumeTextureSliceShader; + } else if (item->useHighDefShader()) { + shader = m_volumeTextureShader; + } else { + shader = m_volumeTextureLowDefShader; + } } else { - shader = m_volumeTextureLowDefShader; + shader = regularShader; } - } else { - shader = regularShader; - } - if (shader != prevShader) - shader->bind(); + if (shader != prevShader) + shader->bind(); #endif - shader->setUniformValue(shader->model(), modelMatrix); - shader->setUniformValue(shader->MVP(), MVPMatrix); - shader->setUniformValue(shader->nModel(), itModelMatrix.inverted().transposed()); + shader->setUniformValue(shader->model(), modelMatrix); + shader->setUniformValue(shader->MVP(), MVPMatrix); + shader->setUniformValue(shader->nModel(), itModelMatrix.inverted().transposed()); - if (item->isBlendNeeded()) { - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + if (item->isBlendNeeded()) { + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); #if !defined(QT_OPENGL_ES_2) - if (!item->isVolume()) + if (!item->isVolume()) #endif - glDisable(GL_CULL_FACE); - } else { - glDisable(GL_BLEND); - glEnable(GL_CULL_FACE); - } + glDisable(GL_CULL_FACE); + } else { + glDisable(GL_BLEND); + glEnable(GL_CULL_FACE); + } #if !defined(QT_OPENGL_ES_2) - if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone && !item->isVolume()) { - // Set shadow shader bindings - shader->setUniformValue(shader->shadowQ(), shadowQuality); - shader->setUniformValue(shader->depth(), depthProjectionViewMatrix * modelMatrix); - shader->setUniformValue(shader->lightS(), m_cachedTheme->lightStrength() / 10.0f); - m_drawer->drawObject(shader, item->mesh(), item->texture(), depthTexture); - } else + if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone && !item->isVolume()) { + // Set shadow shader bindings + shader->setUniformValue(shader->shadowQ(), shadowQuality); + shader->setUniformValue(shader->depth(), depthProjectionViewMatrix * modelMatrix); + shader->setUniformValue(shader->lightS(), m_cachedTheme->lightStrength() / 10.0f); + m_drawer->drawObject(shader, item->mesh(), item->texture(), depthTexture); + } else #else - Q_UNUSED(depthTexture) - Q_UNUSED(shadowQuality) + Q_UNUSED(depthTexture) + Q_UNUSED(shadowQuality) #endif - { - // Set shadowless shader bindings + { + // Set shadowless shader bindings #if !defined(QT_OPENGL_ES_2) - if (item->isVolume()) { - // Volume shaders repurpose light position for camera position relative to item - QVector3D cameraPos = m_cachedScene->activeCamera()->position(); - cameraPos = MVPMatrix.inverted().map(cameraPos); - shader->setUniformValue(shader->cameraPositionRelativeToModel(), -cameraPos); - GLint color8Bit = (item->textureFormat() == QImage::Format_Indexed8) ? 1 : 0; - if (color8Bit) { - shader->setUniformValueArray(shader->colorIndex(), - item->colorTable().constData(), 256); - } - shader->setUniformValue(shader->color8Bit(), color8Bit); - shader->setUniformValue(shader->alphaMultiplier(), item->alphaMultiplier()); - shader->setUniformValue(shader->preserveOpacity(), - item->preserveOpacity() ? 1 : 0); - if (shader == m_volumeTextureSliceShader) { - QVector3D slices((float(item->sliceIndexX()) + 0.5f) - / float(item->textureWidth()) * 2.0 - 1.0, - (float(item->sliceIndexY()) + 0.5f) - / float(item->textureHeight()) * 2.0 - 1.0, - (float(item->sliceIndexZ()) + 0.5f) - / float(item->textureDepth()) * 2.0 - 1.0); - shader->setUniformValue(shader->volumeSliceIndices(), slices); - } else { - // Precalculate texture dimensions so we can optimize - // ray stepping to hit every texture layer. - QVector3D textureDimensions(1.0f / float(item->textureWidth()), - 1.0f / float(item->textureHeight()), - 1.0f / float(item->textureDepth())); - - // Worst case scenario sample count - int sampleCount; - if (shader == m_volumeTextureLowDefShader) { - sampleCount = qMax(item->textureWidth(), - qMax(item->textureDepth(), item->textureHeight())); - // Further improve speed with big textures by simply dropping every - // other sample: - if (sampleCount > 256) - sampleCount /= 2; + if (item->isVolume()) { + // Volume shaders repurpose light position for camera position relative to item + QVector3D cameraPos = m_cachedScene->activeCamera()->position(); + cameraPos = MVPMatrix.inverted().map(cameraPos); + shader->setUniformValue(shader->cameraPositionRelativeToModel(), -cameraPos); + GLint color8Bit = (item->textureFormat() == QImage::Format_Indexed8) ? 1 : 0; + if (color8Bit) { + shader->setUniformValueArray(shader->colorIndex(), + item->colorTable().constData(), 256); + } + shader->setUniformValue(shader->color8Bit(), color8Bit); + shader->setUniformValue(shader->alphaMultiplier(), item->alphaMultiplier()); + shader->setUniformValue(shader->preserveOpacity(), + item->preserveOpacity() ? 1 : 0); + if (shader == m_volumeTextureSliceShader) { + QVector3D slices((float(item->sliceIndexX()) + 0.5f) + / float(item->textureWidth()) * 2.0 - 1.0, + (float(item->sliceIndexY()) + 0.5f) + / float(item->textureHeight()) * 2.0 - 1.0, + (float(item->sliceIndexZ()) + 0.5f) + / float(item->textureDepth()) * 2.0 - 1.0); + shader->setUniformValue(shader->volumeSliceIndices(), slices); } else { - sampleCount = item->textureWidth() + item->textureHeight() - + item->textureDepth(); + // Precalculate texture dimensions so we can optimize + // ray stepping to hit every texture layer. + QVector3D textureDimensions(1.0f / float(item->textureWidth()), + 1.0f / float(item->textureHeight()), + 1.0f / float(item->textureDepth())); + + // Worst case scenario sample count + int sampleCount; + if (shader == m_volumeTextureLowDefShader) { + sampleCount = qMax(item->textureWidth(), + qMax(item->textureDepth(), item->textureHeight())); + // Further improve speed with big textures by simply dropping every + // other sample: + if (sampleCount > 256) + sampleCount /= 2; + } else { + sampleCount = item->textureWidth() + item->textureHeight() + + item->textureDepth(); + } + shader->setUniformValue(shader->textureDimensions(), textureDimensions); + shader->setUniformValue(shader->sampleCount(), sampleCount); } - shader->setUniformValue(shader->textureDimensions(), textureDimensions); - shader->setUniformValue(shader->sampleCount(), sampleCount); - } - m_drawer->drawObject(shader, item->mesh(), 0, 0, item->texture()); - } else + m_drawer->drawObject(shader, item->mesh(), 0, 0, item->texture()); + } else #endif - { - shader->setUniformValue(shader->lightS(), m_cachedTheme->lightStrength()); - m_drawer->drawObject(shader, item->mesh(), item->texture()); + { + shader->setUniformValue(shader->lightS(), m_cachedTheme->lightStrength()); + m_drawer->drawObject(shader, item->mesh(), item->texture()); + } } + } else if (RenderingSelection == state) { + // Selection render + shader->setUniformValue(shader->MVP(), MVPMatrix); + QVector4D itemColor = indexToSelectionColor(item->index()); + itemColor.setW(customItemAlpha); + itemColor /= 255.0f; + shader->setUniformValue(shader->color(), itemColor); + m_drawer->drawObject(shader, item->mesh()); + } else if (item->isShadowCasting()) { + // Depth render + shader->setUniformValue(shader->MVP(), depthProjectionViewMatrix * modelMatrix); + m_drawer->drawObject(shader, item->mesh()); } - } else if (RenderingSelection == state) { - // Selection render - shader->setUniformValue(shader->MVP(), MVPMatrix); - QVector4D itemColor = indexToSelectionColor(item->index()); - itemColor.setW(customItemAlpha); - itemColor /= 255.0f; - shader->setUniformValue(shader->color(), itemColor); - m_drawer->drawObject(shader, item->mesh()); - } else if (item->isShadowCasting()) { - // Depth render - shader->setUniformValue(shader->MVP(), depthProjectionViewMatrix * modelMatrix); - m_drawer->drawObject(shader, item->mesh()); } + loopCount++; + if (!volumeDetected) + loopCount++; // Skip second run if no volumes detected } if (RenderingNormal == state) { diff --git a/tests/volumetrictest/volumetrictest.cpp b/tests/volumetrictest/volumetrictest.cpp index 236273d7..53b0a875 100644 --- a/tests/volumetrictest/volumetrictest.cpp +++ b/tests/volumetrictest/volumetrictest.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -70,7 +71,17 @@ VolumetricModifier::VolumetricModifier(Q3DScatter *scatter) m_graph->addCustomItem(m_plainItem); //m_graph->setMeasureFps(true); - + // Create label to cut through the volume 3 + QCustom3DLabel *label = new QCustom3DLabel; + label->setText(QStringLiteral("FOO BAR - FOO BAR - FOO BAR")); + QFont font; + font.setPixelSize(100); + label->setFont(font); + label->setScaling(QVector3D(2.0f, 2.0f, 0.0f)); + label->setRotationAxisAndAngle(QVector3D(0.0f, 1.0f, 0.0f), 45.0f); + label->setPosition(m_volumeItem3->position()); + label->setPositionAbsolute(true); + m_graph->addCustomItem(label); QObject::connect(m_graph, &QAbstract3DGraph::currentFpsChanged, this, &VolumetricModifier::handleFpsChange); -- cgit v1.2.3 From 2af50e5903f631137117879a7d7945ec006e14ce Mon Sep 17 00:00:00 2001 From: Mika Salmela Date: Wed, 3 Sep 2014 12:53:00 +0300 Subject: Remove undeclared identifier Task-number: QTRD-3303 Change-Id: I8f43df8f0d90997c05ffaf93e451a52baacefa63 Reviewed-by: Miikka Heikkinen --- src/datavisualization/engine/abstract3drenderer.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/datavisualization/engine/abstract3drenderer.cpp b/src/datavisualization/engine/abstract3drenderer.cpp index 640502ad..dfb73d99 100644 --- a/src/datavisualization/engine/abstract3drenderer.cpp +++ b/src/datavisualization/engine/abstract3drenderer.cpp @@ -1175,10 +1175,6 @@ void Abstract3DRenderer::drawCustomItems(RenderingState state, GLfloat shadowQuality, GLfloat reflection) { -#if defined(QT_OPENGL_ES_2) - Q_UNUSED(volumeShader) - Q_UNUSED(volumeSliceShader) -#endif if (m_customRenderCache.isEmpty()) return; -- cgit v1.2.3 From 28777a99f79bc9db1a28e1b93080b005be03353b Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Wed, 3 Sep 2014 13:37:31 +0300 Subject: Allow setting volume subtexture along any axis Change-Id: Iafaac6bd2106253bec913d1d9ee8a3f40e339adf Reviewed-by: Mika Salmela --- src/datavisualization/data/qcustom3dvolume.cpp | 130 +++++++++++++++++++------ src/datavisualization/data/qcustom3dvolume.h | 4 +- tests/volumetrictest/logo_no_padding.png | Bin 0 -> 2278 bytes tests/volumetrictest/main.cpp | 10 +- tests/volumetrictest/volumetrictest.cpp | 74 ++++++++++++-- tests/volumetrictest/volumetrictest.h | 3 + tests/volumetrictest/volumetrictest.qrc | 1 + 7 files changed, 183 insertions(+), 39 deletions(-) create mode 100644 tests/volumetrictest/logo_no_padding.png diff --git a/src/datavisualization/data/qcustom3dvolume.cpp b/src/datavisualization/data/qcustom3dvolume.cpp index ec5129ff..393533c0 100644 --- a/src/datavisualization/data/qcustom3dvolume.cpp +++ b/src/datavisualization/data/qcustom3dvolume.cpp @@ -428,7 +428,8 @@ QVector QCustom3DVolume::colorTable() const * \note Each X-line of the data needs to be 32bit aligned. If the textureFormat is * QImage::Format_Indexed8 and textureWidth is not divisible by four, padding bytes need * to be added to each X-line of the \a data. You can get the padded byte count with - * textureDataWidth() function. + * textureDataWidth() function. The padding bytes should indicate an fully transparent color + * to avoid rendering artifacts. * * Defaults to \c{0}. * @@ -531,29 +532,76 @@ QVector *QCustom3DVolume::textureData() const } /*! - * This function allows setting a single 2D subtexture of the 3D texture. - * The \a depthIndex parameter specifies the subtexture to set. - * The texture\a data must be in the format specified by textureFormat property and have size of - * (\c{textureDataWidth * textureHeight * texture format color depth in bytes}). + * This function allows setting a single 2D subtexture of the 3D texture along the specified + * \a axis of the volume. + * The \a index parameter specifies the subtexture to set. + * The texture \a data must be in the format specified by textureFormat property and have size of + * the cross-section of the volume texture along the specified axis multiplied by + * the texture format color depth in bytes. + * The \a data is expected to be ordered similarly to the data in images produced by renderSlice() + * method along the same axis. * - * \note Each X-line of the data needs to be 32bit aligned. If the textureFormat is - * QImage::Format_Indexed8 and textureWidth is not divisible by four, padding bytes need - * to be added to each X-line of the \a data. + * \note Each X-line of the data needs to be 32bit aligned when targeting Y-axis or Z-axis. + * If the textureFormat is QImage::Format_Indexed8 and textureWidth is not divisible by four, + * padding bytes need to be added to each X-line of the \a data in cases it is not already + * properly aligned. The padding bytes should indicate an fully transparent color to avoid + * rendering artifacts. * - * \sa textureData + * \sa textureData, renderSlice() */ -void QCustom3DVolume::setSubTextureData(int depthIndex, const uchar *data) +void QCustom3DVolume::setSubTextureData(Qt::Axis axis, int index, const uchar *data) { if (data) { - int frameSize = textureDataWidth() * dptr()->m_textureHeight; - int startIndex = depthIndex * frameSize; + int lineSize = textureDataWidth(); + int frameSize = lineSize * dptr()->m_textureHeight; + int dataSize = dptr()->m_textureData->size(); + int pixelWidth = (dptr()->m_textureFormat == QImage::Format_Indexed8) ? 1 : 4; + int targetIndex; + uchar *dataPtr = dptr()->m_textureData->data(); + bool invalid = (index < 0); + if (axis == Qt::XAxis) { + targetIndex = index * pixelWidth; + if (index >= dptr()->m_textureWidth + || (frameSize * (dptr()->m_textureDepth - 1) + targetIndex) > dataSize) { + invalid = true; + } + } else if (axis == Qt::YAxis) { + targetIndex = (index * lineSize) + (frameSize * (dptr()->m_textureDepth - 1)); + if (index >= dptr()->m_textureHeight || (targetIndex + lineSize > dataSize)) + invalid = true; + } else { + targetIndex = index * frameSize; + if (index >= dptr()->m_textureDepth || ((targetIndex + frameSize) > dataSize)) + invalid = true; + } - if (depthIndex >= dptr()->m_textureDepth - || (startIndex + frameSize) > dptr()->m_textureData->size()) { + if (invalid) { qWarning() << __FUNCTION__ << "Attempted to set invalid subtexture."; } else { - void *subTexPtr = dptr()->m_textureData->data() + startIndex; - memcpy(subTexPtr, static_cast(data), frameSize); + const uchar *sourcePtr = data; + uchar *targetPtr = dataPtr + targetIndex; + if (axis == Qt::XAxis) { + int targetWidth = dptr()->m_textureDepth; + int targetHeight = dptr()->m_textureHeight; + for (int i = 0; i < targetHeight; i++) { + targetPtr = dataPtr + targetIndex + (lineSize * i); + for (int j = 0; j < targetWidth; j++) { + for (int k = 0; k < pixelWidth; k++) + *targetPtr++ = *sourcePtr++; + targetPtr += (frameSize - pixelWidth); + } + } + } else if (axis == Qt::YAxis) { + int targetHeight = dptr()->m_textureDepth; + for (int i = 0; i < targetHeight; i++){ + for (int j = 0; j < lineSize; j++) + *targetPtr++ = *sourcePtr++; + targetPtr -= (frameSize + lineSize); + } + } else { + void *subTexPtr = dataPtr + targetIndex; + memcpy(subTexPtr, static_cast(data), frameSize); + } dptr()->m_dirtyBitsVolume.textureDataDirty = true; emit textureDataChanged(dptr()->m_textureData); emit dptr()->needUpdate(); @@ -564,18 +612,42 @@ void QCustom3DVolume::setSubTextureData(int depthIndex, const uchar *data) } /*! - * This function allows setting a single 2D subtexture of the 3D texture to a source \a image; - * The \a depthIndex parameter specifies the subtexture to set. - * The image must be in the format specified by the textureFormat property if the textureFormat - * is indexed. If the textureFormat is QImage::Format_ARGB32, the image is converted to that format. - * The image must have the size of (\c{textureWidth * textureHeight}). - * - * \sa textureData - */ -void QCustom3DVolume::setSubTextureData(int depthIndex, const QImage &image) -{ - if (image.width() == dptr()->m_textureWidth - && image.height() == dptr()->m_textureHeight + * This function allows setting a single 2D subtexture of the 3D texture along the specified + * \a axis of the volume. + * The \a index parameter specifies the subtexture to set. + * The source \a image must be in the format specified by the textureFormat property if the + * textureFormat is indexed. If the textureFormat is QImage::Format_ARGB32, the image is converted + * to that format. The image must have the size of the cross-section of the volume texture along + * the specified axis. The orientation of the image should correspond to the orientation of + * the slice image produced by renderSlice() method along the same axis. + * + * \note Each X-line of the data needs to be 32bit aligned when targeting Y-axis or Z-axis. + * If the textureFormat is QImage::Format_Indexed8 and textureWidth is not divisible by four, + * padding bytes need to be added to each X-line of the \a data in cases it is not already + * properly aligned. The padding bytes should indicate an fully transparent color to avoid + * rendering artifacts. It is not guaranteed QImage will do this automatically. + * + * \sa textureData, renderSlice() + */ +void QCustom3DVolume::setSubTextureData(Qt::Axis axis, int index, const QImage &image) +{ + int sourceWidth = image.width(); + int sourceHeight = image.height(); + int targetWidth; + int targetHeight; + if (axis == Qt::XAxis) { + targetWidth = dptr()->m_textureDepth; + targetHeight = dptr()->m_textureHeight; + } else if (axis == Qt::YAxis) { + targetWidth = dptr()->m_textureWidth; + targetHeight = dptr()->m_textureDepth; + } else { + targetWidth = dptr()->m_textureWidth; + targetHeight = dptr()->m_textureHeight; + } + + if (sourceWidth == targetWidth + && sourceHeight == targetHeight && (image.format() == dptr()->m_textureFormat || dptr()->m_textureFormat == QImage::Format_ARGB32)) { QImage convertedImage; @@ -585,7 +657,7 @@ void QCustom3DVolume::setSubTextureData(int depthIndex, const QImage &image) } else { convertedImage = image; } - setSubTextureData(depthIndex, convertedImage.bits()); + setSubTextureData(axis, index, convertedImage.bits()); } else { qWarning() << __FUNCTION__ << "Invalid image size or format."; } diff --git a/src/datavisualization/data/qcustom3dvolume.h b/src/datavisualization/data/qcustom3dvolume.h index 23ae07d9..04434263 100644 --- a/src/datavisualization/data/qcustom3dvolume.h +++ b/src/datavisualization/data/qcustom3dvolume.h @@ -76,8 +76,8 @@ public: void setTextureData(QVector *data); QVector *createTextureData(const QVector &images); QVector *textureData() const; - void setSubTextureData(int depthIndex, const uchar *data); - void setSubTextureData(int depthIndex, const QImage &image); + void setSubTextureData(Qt::Axis axis, int index, const uchar *data); + void setSubTextureData(Qt::Axis axis, int index, const QImage &image); void setTextureFormat(QImage::Format format); QImage::Format textureFormat() const; diff --git a/tests/volumetrictest/logo_no_padding.png b/tests/volumetrictest/logo_no_padding.png new file mode 100644 index 00000000..714234aa Binary files /dev/null and b/tests/volumetrictest/logo_no_padding.png differ diff --git a/tests/volumetrictest/main.cpp b/tests/volumetrictest/main.cpp index e838c43a..ba5ba6d3 100644 --- a/tests/volumetrictest/main.cpp +++ b/tests/volumetrictest/main.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include int main(int argc, char **argv) @@ -93,6 +94,9 @@ int main(int argc, char **argv) sliceImageYLabel->setScaledContents(true); sliceImageZLabel->setScaledContents(true); + QPushButton *testSubTextureSetting = new QPushButton(widget); + testSubTextureSetting->setText(QStringLiteral("Test subtexture settings")); + vLayout->addWidget(fpsLabel); vLayout->addWidget(sliceXCheckBox); vLayout->addWidget(sliceXSlider); @@ -102,8 +106,8 @@ int main(int argc, char **argv) vLayout->addWidget(sliceImageYLabel); vLayout->addWidget(sliceZCheckBox); vLayout->addWidget(sliceZSlider); - vLayout->addWidget(sliceImageZLabel, 1, Qt::AlignTop); - + vLayout->addWidget(sliceImageZLabel); + vLayout->addWidget(testSubTextureSetting, 1, Qt::AlignTop); VolumetricModifier *modifier = new VolumetricModifier(graph); modifier->setFpsLabel(fpsLabel); @@ -121,6 +125,8 @@ int main(int argc, char **argv) &VolumetricModifier::adjustSliceY); QObject::connect(sliceZSlider, &QSlider::valueChanged, modifier, &VolumetricModifier::adjustSliceZ); + QObject::connect(testSubTextureSetting, &QPushButton::clicked, modifier, + &VolumetricModifier::testSubtextureSetting); widget->show(); return app.exec(); diff --git a/tests/volumetrictest/volumetrictest.cpp b/tests/volumetrictest/volumetrictest.cpp index 53b0a875..555cc286 100644 --- a/tests/volumetrictest/volumetrictest.cpp +++ b/tests/volumetrictest/volumetrictest.cpp @@ -168,7 +168,67 @@ void VolumetricModifier::handleFpsChange() // const QString sceneDimensionsFormat = QStringLiteral("%1 x %2"); // m_fpsLabel->setText(sceneDimensionsFormat // .arg(m_graph->scene()->viewport().width()) -// .arg(m_graph->scene()->viewport().height())); + // .arg(m_graph->scene()->viewport().height())); +} + +void VolumetricModifier::testSubtextureSetting() +{ + // Setting the rendered Slice as subtexture should result in identical volume + QVector dataBefore = *m_volumeItem->textureData(); + dataBefore[0] = dataBefore.at(0); // Make sure we are detached + + checkRenderCase(1, Qt::XAxis, 56, dataBefore, m_volumeItem); + checkRenderCase(2, Qt::YAxis, 64, dataBefore, m_volumeItem); + checkRenderCase(3, Qt::ZAxis, 87, dataBefore, m_volumeItem); + + checkRenderCase(4, Qt::XAxis, 0, dataBefore, m_volumeItem); + checkRenderCase(5, Qt::YAxis, 0, dataBefore, m_volumeItem); + checkRenderCase(6, Qt::ZAxis, 0, dataBefore, m_volumeItem); + + checkRenderCase(7, Qt::XAxis, m_volumeItem->textureWidth() - 1, dataBefore, m_volumeItem); + checkRenderCase(8, Qt::YAxis, m_volumeItem->textureHeight() - 1, dataBefore, m_volumeItem); + checkRenderCase(9, Qt::ZAxis, m_volumeItem->textureDepth() - 1, dataBefore, m_volumeItem); + + dataBefore = *m_volumeItem2->textureData(); + dataBefore[0] = dataBefore.at(0); // Make sure we are detached + + checkRenderCase(11, Qt::XAxis, 56, dataBefore, m_volumeItem2); + checkRenderCase(12, Qt::YAxis, 64, dataBefore, m_volumeItem2); + checkRenderCase(13, Qt::ZAxis, 87, dataBefore, m_volumeItem2); + + checkRenderCase(14, Qt::XAxis, 0, dataBefore, m_volumeItem2); + checkRenderCase(15, Qt::YAxis, 0, dataBefore, m_volumeItem2); + checkRenderCase(16, Qt::ZAxis, 0, dataBefore, m_volumeItem2); + + checkRenderCase(17, Qt::XAxis, m_volumeItem2->textureWidth() - 1, dataBefore, m_volumeItem2); + checkRenderCase(18, Qt::YAxis, m_volumeItem2->textureHeight() - 1, dataBefore, m_volumeItem2); + checkRenderCase(19, Qt::ZAxis, m_volumeItem2->textureDepth() - 1, dataBefore, m_volumeItem2); + + // Do some visible swaps on volume 3 + QImage slice = m_volumeItem3->renderSlice(Qt::XAxis, 144); + slice = slice.mirrored(); + m_volumeItem3->setSubTextureData(Qt::XAxis, 144, slice); + + slice = m_volumeItem3->renderSlice(Qt::YAxis, 80); + slice = slice.mirrored(); + m_volumeItem3->setSubTextureData(Qt::YAxis, 80, slice); + + slice = m_volumeItem3->renderSlice(Qt::ZAxis, 190); + slice = slice.mirrored(true, false); + m_volumeItem3->setSubTextureData(Qt::ZAxis, 190, slice); +} + +void VolumetricModifier::checkRenderCase(int id, Qt::Axis axis, int index, + const QVector &dataBefore, + QCustom3DVolume *volumeItem) +{ + QImage slice = volumeItem->renderSlice(axis, index); + volumeItem->setSubTextureData(axis, index, slice); + + if (dataBefore == *volumeItem->textureData()) + qDebug() << __FUNCTION__ << "Case:" << id << "Vectors identical"; + else + qDebug() << __FUNCTION__ << "Case:" << id << "BUG: VECTORS DIFFER!"; } void VolumetricModifier::createVolume() @@ -179,7 +239,8 @@ void VolumetricModifier::createVolume() m_volumeItem->setPosition(QVector3D(-0.5f, 0.6f, 0.0f)); QImage logo; - logo.load(QStringLiteral(":/logo.png")); + logo.load(QStringLiteral(":/logo_no_padding.png")); + //logo.load(QStringLiteral(":/logo.png")); qDebug() << "image dimensions:" << logo.width() << logo.height() << logo.byteCount() << (logo.width() * logo.height()) << logo.bytesPerLine(); @@ -233,7 +294,7 @@ void VolumetricModifier::createVolume() // Change one picture using subtexture replacement QImage flipped = logo.mirrored(); - m_volumeItem->setSubTextureData(100, flipped); + m_volumeItem->setSubTextureData(Qt::ZAxis, 100, flipped); // Clean up the two extra pixels p = data + width - 1; @@ -331,7 +392,8 @@ void VolumetricModifier::createAnotherVolume() m_volumeItem2->setPosition(QVector3D(0.5f, -0.5f, 0.0f)); QImage logo; - logo.load(QStringLiteral(":/logo.png")); + logo.load(QStringLiteral(":/logo_no_padding.png")); + //logo.load(QStringLiteral(":/logo.png")); logo = logo.convertToFormat(QImage::Format_ARGB8555_Premultiplied); qDebug() << "second image dimensions:" << logo.width() << logo.height() << logo.byteCount() << (logo.width() * logo.height()) @@ -361,8 +423,8 @@ void VolumetricModifier::createAnotherVolume() // Change one picture using subtexture replacement QImage flipped = logo.mirrored(); - m_volumeItem2->setSubTextureData(100, flipped); - m_volumeItem2->setAlphaMultiplier(0.2f); + m_volumeItem2->setSubTextureData(Qt::ZAxis, 100, flipped); + //m_volumeItem2->setAlphaMultiplier(0.2f); m_volumeItem2->setPreserveOpacity(false); } diff --git a/tests/volumetrictest/volumetrictest.h b/tests/volumetrictest/volumetrictest.h index f21fd528..b1b98455 100644 --- a/tests/volumetrictest/volumetrictest.h +++ b/tests/volumetrictest/volumetrictest.h @@ -45,11 +45,14 @@ public slots: void adjustSliceY(int value); void adjustSliceZ(int value); void handleFpsChange(); + void testSubtextureSetting(); private: void createVolume(); void createAnotherVolume(); void createYetAnotherVolume(); + void checkRenderCase(int id, Qt::Axis axis, int index, const QVector &dataBefore, + QCustom3DVolume *volumeItem); Q3DScatter *m_graph; QCustom3DVolume *m_volumeItem; diff --git a/tests/volumetrictest/volumetrictest.qrc b/tests/volumetrictest/volumetrictest.qrc index 5b9623f0..7cd8533b 100644 --- a/tests/volumetrictest/volumetrictest.qrc +++ b/tests/volumetrictest/volumetrictest.qrc @@ -2,5 +2,6 @@ logo.png cubeFilledFlat.obj + logo_no_padding.png -- cgit v1.2.3 From 18413de96ea907ea5c7defdcb40248fdfbaa4de7 Mon Sep 17 00:00:00 2001 From: Mika Salmela Date: Wed, 3 Sep 2014 15:00:07 +0300 Subject: ES2 gradient support for static optimization Change-Id: I3d5d5d006a26225a3a7ab5795fceb97630db0011 Reviewed-by: Miikka Heikkinen --- .../engine/abstract3drenderer.cpp | 16 +++++++++++---- src/datavisualization/engine/engine.qrc | 1 + src/datavisualization/engine/scatter3drenderer.cpp | 23 ++++++++++++++++++++++ src/datavisualization/engine/scatter3drenderer_p.h | 2 ++ .../engine/shaders/point_ES2_UV.vert | 12 +++++++++++ 5 files changed, 50 insertions(+), 4 deletions(-) create mode 100644 src/datavisualization/engine/shaders/point_ES2_UV.vert diff --git a/src/datavisualization/engine/abstract3drenderer.cpp b/src/datavisualization/engine/abstract3drenderer.cpp index dfb73d99..3d5ccba1 100644 --- a/src/datavisualization/engine/abstract3drenderer.cpp +++ b/src/datavisualization/engine/abstract3drenderer.cpp @@ -329,10 +329,18 @@ void Abstract3DRenderer::reInitShaders() QStringLiteral(":/shaders/fragmentTexture3DLowDef"), QStringLiteral(":/shaders/fragmentTexture3DSlice")); #else - initGradientShaders(QStringLiteral(":/shaders/vertex"), - QStringLiteral(":/shaders/fragmentColorOnYES2")); - initShaders(QStringLiteral(":/shaders/vertex"), - QStringLiteral(":/shaders/fragmentES2")); + if (m_cachedOptimizationHint.testFlag(QAbstract3DGraph::OptimizationStatic) + && qobject_cast(this)) { + initGradientShaders(QStringLiteral(":/shaders/vertexTexture"), + QStringLiteral(":/shaders/fragmentTextureES2")); + initBackgroundShaders(QStringLiteral(":/shaders/vertexNoMatrices"), + QStringLiteral(":/shaders/fragmentES2")); + } else { + initGradientShaders(QStringLiteral(":/shaders/vertex"), + QStringLiteral(":/shaders/fragmentColorOnYES2")); + initShaders(QStringLiteral(":/shaders/vertex"), + QStringLiteral(":/shaders/fragmentES2")); + } initBackgroundShaders(QStringLiteral(":/shaders/vertex"), QStringLiteral(":/shaders/fragmentES2")); initCustomItemShaders(QStringLiteral(":/shaders/vertexTexture"), diff --git a/src/datavisualization/engine/engine.qrc b/src/datavisualization/engine/engine.qrc index 0936bc16..eeddf92e 100644 --- a/src/datavisualization/engine/engine.qrc +++ b/src/datavisualization/engine/engine.qrc @@ -65,5 +65,6 @@ shaders/shadowNoMatrices.vert shaders/defaultNoMatrices.vert shaders/texture3dlowdef.frag + shaders/point_ES2_UV.vert diff --git a/src/datavisualization/engine/scatter3drenderer.cpp b/src/datavisualization/engine/scatter3drenderer.cpp index 8c4644c6..ba1019ad 100644 --- a/src/datavisualization/engine/scatter3drenderer.cpp +++ b/src/datavisualization/engine/scatter3drenderer.cpp @@ -50,6 +50,7 @@ Scatter3DRenderer::Scatter3DRenderer(Scatter3DController *controller) m_selectionShader(0), m_backgroundShader(0), m_labelShader(0), + m_staticGradientPointShader(0), m_bgrTexture(0), m_selectionTexture(0), m_depthFrameBuffer(0), @@ -93,6 +94,7 @@ Scatter3DRenderer::~Scatter3DRenderer() delete m_selectionShader; delete m_backgroundShader; delete m_labelShader; + delete m_staticGradientPointShader; } void Scatter3DRenderer::initializeOpenGL() @@ -348,6 +350,13 @@ void Scatter3DRenderer::updateOptimizationHint(QAbstract3DGraph::OptimizationHin Abstract3DRenderer::updateOptimizationHint(hint); Abstract3DRenderer::reInitShaders(); + +#if defined(QT_OPENGL_ES_2) + if (hint.testFlag(QAbstract3DGraph::OptimizationStatic) && !m_staticGradientPointShader) { + initStaticPointShaders(QStringLiteral(":/shaders/vertexPointES2_UV"), + QStringLiteral(":/shaders/fragmentLabel")); + } +#endif } void Scatter3DRenderer::resetClickedStatus() @@ -859,7 +868,11 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) } if (!optimizationDefault && rangeGradientPoints) { +#if !defined(QT_OPENGL_ES_2) dotShader = m_labelShader; +#else + dotShader = m_staticGradientPointShader; +#endif dotShader->bind(); gradientTexture = cache->baseGradientTexture(); } @@ -2208,6 +2221,16 @@ void Scatter3DRenderer::initLabelShaders(const QString &vertexShader, const QStr m_labelShader->initialize(); } +void Scatter3DRenderer::initStaticPointShaders(const QString &vertexShader, + const QString &fragmentShader) +{ + if (m_staticGradientPointShader) + delete m_staticGradientPointShader; + m_staticGradientPointShader = new ShaderHelper(this, vertexShader, fragmentShader); + m_staticGradientPointShader->initialize(); +} + + void Scatter3DRenderer::selectionColorToSeriesAndIndex(const QVector4D &color, int &index, QAbstract3DSeries *&series) diff --git a/src/datavisualization/engine/scatter3drenderer_p.h b/src/datavisualization/engine/scatter3drenderer_p.h index 353dc098..3fc517d0 100644 --- a/src/datavisualization/engine/scatter3drenderer_p.h +++ b/src/datavisualization/engine/scatter3drenderer_p.h @@ -60,6 +60,7 @@ private: ShaderHelper *m_selectionShader; ShaderHelper *m_backgroundShader; ShaderHelper *m_labelShader; + ShaderHelper *m_staticGradientPointShader; GLuint m_bgrTexture; GLuint m_selectionTexture; GLuint m_depthFrameBuffer; @@ -129,6 +130,7 @@ private: void initSelectionShader(); void initBackgroundShaders(const QString &vertexShader, const QString &fragmentShader); void initLabelShaders(const QString &vertexShader, const QString &fragmentShader); + void initStaticPointShaders(const QString &vertexShader, const QString &fragmentShader); void initSelectionBuffer(); #if !defined(QT_OPENGL_ES_2) void initDepthShader(); diff --git a/src/datavisualization/engine/shaders/point_ES2_UV.vert b/src/datavisualization/engine/shaders/point_ES2_UV.vert new file mode 100644 index 00000000..f181db4f --- /dev/null +++ b/src/datavisualization/engine/shaders/point_ES2_UV.vert @@ -0,0 +1,12 @@ +uniform highp mat4 MVP; + +attribute highp vec3 vertexPosition_mdl; +attribute highp vec2 vertexUV; + +varying highp vec2 UV; + +void main() { + gl_PointSize = 5.0; + gl_Position = MVP * vec4(vertexPosition_mdl, 1.0); + UV = vertexUV; +} -- cgit v1.2.3 From bb30ea555c71604de9a2bc5096fa35c9532b26bd Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Thu, 4 Sep 2014 15:00:28 +0300 Subject: Add possibility to scale custom items according to data ranges MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Proper behavior of volume objects that are shown only partially will be added in a separate patch later. Change-Id: I1fcd98faa6c4a7d09e3fef1645ed9816ff54654f Reviewed-by: Tomi Korpipää --- .../datavisualization/volumetric/volumetric.cpp | 30 +++++--- src/datavisualization/data/customrenderitem.cpp | 4 +- src/datavisualization/data/customrenderitem_p.h | 15 +++- src/datavisualization/data/qcustom3ditem.cpp | 85 ++++++++++++++++++--- src/datavisualization/data/qcustom3ditem.h | 5 ++ src/datavisualization/data/qcustom3ditem_p.h | 4 +- src/datavisualization/data/qcustom3dvolume.cpp | 4 +- .../engine/abstract3drenderer.cpp | 60 +++++++++++---- .../engine/abstract3drenderer_p.h | 2 + .../datavisualizationqml2_plugin.cpp | 1 + tests/volumetrictest/main.cpp | 34 +++++++++ tests/volumetrictest/volumetrictest.cpp | 89 ++++++++++++++++++---- tests/volumetrictest/volumetrictest.h | 4 + 13 files changed, 281 insertions(+), 56 deletions(-) diff --git a/examples/datavisualization/volumetric/volumetric.cpp b/examples/datavisualization/volumetric/volumetric.cpp index 156e0bf4..eb8f2188 100644 --- a/examples/datavisualization/volumetric/volumetric.cpp +++ b/examples/datavisualization/volumetric/volumetric.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -40,6 +41,7 @@ const int mineShaftDiameter(1); const int airColorIndex(254); const int mineShaftColorIndex(255); const int layerColorThickness(60); +const int heightToColorDiv(128); const int magmaColorsMin(0); const int magmaColorsMax(layerColorThickness); const int aboveWaterGroundColorsMin(magmaColorsMax + 1); @@ -93,7 +95,10 @@ VolumetricModifier::VolumetricModifier(Q3DScatter *scatter) excavateMineShaft(lowDetailSize, 0, m_mineShaftArray.size(), m_lowDetailData); m_volumeItem = new QCustom3DVolume; - m_volumeItem->setScaling(QVector3D(2.0f, 1.0f, 2.0f)); + m_volumeItem->setScaling(QVector3D(m_graph->axisX()->max() - m_graph->axisX()->min(), + m_graph->axisY()->max() - m_graph->axisY()->min(), + m_graph->axisZ()->max() - m_graph->axisZ()->min())); + m_volumeItem->setScalingAbsolute(false); m_volumeItem->setTextureWidth(lowDetailSize); m_volumeItem->setTextureHeight(lowDetailSize / 2); m_volumeItem->setTextureDepth(lowDetailSize); @@ -108,11 +113,14 @@ VolumetricModifier::VolumetricModifier(Q3DScatter *scatter) if (i < magmaColorsMax) { m_colorTable1[i] = qRgba(130 - (i * 2), 0, 0, 255); } else if (i < aboveWaterGroundColorsMax) { - m_colorTable1[i] = qRgba(0, ((i - magmaColorsMax) * 2) + 120, 0, terrainTransparency); + m_colorTable1[i] = qRgba((i - magmaColorsMax) * 4, + ((i - magmaColorsMax) * 2) + 120, + (i - magmaColorsMax) * 5, terrainTransparency); } else if (i < underWaterGroundColorsMax) { - m_colorTable1[i] = qRgba(((i - aboveWaterGroundColorsMax) * 2) + 30, - ((i - aboveWaterGroundColorsMax) * 2) + 100, - ((i - aboveWaterGroundColorsMax) * 2) + 30, terrainTransparency); + m_colorTable1[i] = qRgba(((layerColorThickness - i - aboveWaterGroundColorsMax)) + 70, + ((layerColorThickness - i - aboveWaterGroundColorsMax) * 2) + 20, + ((layerColorThickness - i - aboveWaterGroundColorsMax)) + 50, + terrainTransparency); } else if (i < waterColorsMax) { m_colorTable1[i] = qRgba(0, 0, ((i - underWaterGroundColorsMax) * 2) + 120, terrainTransparency); @@ -471,19 +479,19 @@ int VolumetricModifier::createVolume(int textureSize, int startIndex, int count, int height((layerDataSize - (j * 2 * multiplier)) / 2); if (height < magmaHeights.at(k)) { // Magma layer - colorIndex = int((float(height) / colorTableSize) + colorIndex = int((float(height) / heightToColorDiv) * float(layerColorThickness)) + magmaColorsMin; - } else if (height <= groundHeights.at(k) && height <= waterHeights.at(k)) { + } else if (height < groundHeights.at(k) && height < waterHeights.at(k)) { // Ground layer below water - colorIndex = int((float(waterHeights.at(k) - height) / colorTableSize) + colorIndex = int((float(waterHeights.at(k) - height) / heightToColorDiv) * float(layerColorThickness)) + underWaterGroundColorsMin; } else if (height <= waterHeights.at(k)) { // Water layer where water goes over ground - colorIndex = int((float(height - magmaHeights.at(k)) / colorTableSize) + colorIndex = int((float(height - magmaHeights.at(k)) / heightToColorDiv) * float(layerColorThickness)) + waterColorsMin; } else if (height <= groundHeights.at(k)) { // Ground above water - colorIndex = int((float(height - waterHeights.at(k)) / colorTableSize) + colorIndex = int((float(height - waterHeights.at(k)) / heightToColorDiv) * float(layerColorThickness)) + aboveWaterGroundColorsMin; } else { // Rest is air @@ -499,7 +507,7 @@ int VolumetricModifier::createVolume(int textureSize, int startIndex, int count, } int VolumetricModifier::excavateMineShaft(int textureSize, int startIndex, int count, - QVector *textureData) + QVector *textureData) { int endIndex = startIndex + count; if (endIndex > m_mineShaftArray.size()) diff --git a/src/datavisualization/data/customrenderitem.cpp b/src/datavisualization/data/customrenderitem.cpp index 555f48b7..64194bac 100644 --- a/src/datavisualization/data/customrenderitem.cpp +++ b/src/datavisualization/data/customrenderitem.cpp @@ -23,7 +23,8 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION CustomRenderItem::CustomRenderItem() : AbstractRenderItem(), m_texture(0), - m_absolute(false), + m_positionAbsolute(false), + m_scalingAbsolute(true), m_object(0), m_needBlend(true), m_visible(true), @@ -33,6 +34,7 @@ CustomRenderItem::CustomRenderItem() m_isFacingCamera(false), m_item(0), m_renderer(0), + m_labelItem(false), m_textureWidth(0), m_textureHeight(0), m_textureDepth(0), diff --git a/src/datavisualization/data/customrenderitem_p.h b/src/datavisualization/data/customrenderitem_p.h index 5024270a..8ea8e894 100644 --- a/src/datavisualization/data/customrenderitem_p.h +++ b/src/datavisualization/data/customrenderitem_p.h @@ -51,10 +51,14 @@ public: inline ObjectHelper *mesh() const { return m_object; } inline void setScaling(const QVector3D &scaling) { m_scaling = scaling; } inline QVector3D scaling() const { return m_scaling; } + inline void setOrigScaling(const QVector3D &scaling) { m_origScaling = scaling; } + inline QVector3D origScaling() const { return m_origScaling; } inline void setPosition(const QVector3D &position) { m_position = position; } inline QVector3D position() const { return m_position; } - inline void setPositionAbsolute(bool absolute) { m_absolute = absolute; } - inline bool isPositionAbsolute() const { return m_absolute; } + inline void setPositionAbsolute(bool absolute) { m_positionAbsolute = absolute; } + inline bool isPositionAbsolute() const { return m_positionAbsolute; } + inline void setScalingAbsolute(bool absolute) { m_scalingAbsolute = absolute; } + inline bool isScalingAbsolute() const { return m_scalingAbsolute; } inline void setBlendNeeded(bool blend) { m_needBlend = blend; } inline bool isBlendNeeded() const { return m_needBlend; } inline void setVisible(bool visible) { m_visible = visible; } @@ -70,6 +74,8 @@ public: inline void setFacingCamera(bool facing) { m_isFacingCamera = facing; } inline bool isFacingCamera() const { return m_isFacingCamera; } inline void setRenderer(Abstract3DRenderer *renderer) { m_renderer = renderer; } + inline void setLabelItem(bool isLabel) { m_labelItem = isLabel; } + inline bool isLabel() const { return m_labelItem; } // Volume specific inline void setTextureWidth(int width) { m_textureWidth = width; } @@ -104,8 +110,10 @@ private: GLuint m_texture; QVector3D m_scaling; + QVector3D m_origScaling; QVector3D m_position; - bool m_absolute; + bool m_positionAbsolute; + bool m_scalingAbsolute; ObjectHelper *m_object; // shared reference bool m_needBlend; bool m_visible; @@ -115,6 +123,7 @@ private: bool m_isFacingCamera; QCustom3DItem *m_item; Abstract3DRenderer *m_renderer; + bool m_labelItem; // Volume specific int m_textureWidth; diff --git a/src/datavisualization/data/qcustom3ditem.cpp b/src/datavisualization/data/qcustom3ditem.cpp index cb843b62..64cb4531 100644 --- a/src/datavisualization/data/qcustom3ditem.cpp +++ b/src/datavisualization/data/qcustom3ditem.cpp @@ -62,11 +62,13 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION * * Holds the item \a position as a vector3d. Defaults to \c {vector3d(0.0, 0.0, 0.0)}. * - * Item position is either in data coordinates or in absolute coordinates, depending on + * Item position is either in data coordinates or in absolute coordinates, depending on the * positionAbsolute property. When using absolute coordinates, values between \c{-1.0...1.0} are * within axis ranges. * - * \note Items positioned outside any axis range are not rendered if positionAbsolute is \c{false}. + * \note Items positioned outside any axis range are not rendered if positionAbsolute is \c{false}, + * unless the item is a Custom3DVolume that would be partially visible. In that case, the visible + * portion of the volume will be rendered. * * \sa positionAbsolute */ @@ -83,8 +85,31 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION /*! \qmlproperty vector3d Custom3DItem::scaling * * Holds the item \a scaling as a vector3d. Defaults to \c {vector3d(0.1, 0.1, 0.1)}. - * The default value sets the item to 10% of the height of the graph, provided the item size is - * normalized. + * + * Item scaling is either in data values or in absolute values, depending on the + * scalingAbsolute property. The default vector interpreted as absolute values sets the item to + * 10% of the height of the graph, provided the item mesh is normalized and the graph aspect ratios + * haven't been changed from the defaults. + * + * \sa scalingAbsolute + */ + +/*! \qmlproperty bool Custom3DItem::scalingAbsolute + * \since QtDataVisualization 1.2 + * + * This property dictates if item scaling is to be handled in data values or in absolute + * values. Defaults to \c{true}. Items with absolute scaling will be rendered at the same + * size, regardless of axis ranges. Items with data scaling will change their apparent size + * according to the axis ranges. If positionAbsolute value is \c{true}, this property is ignored + * and scaling is interpreted as an absolute value. + * + * \note: Only absolute scaling is supported for Custom3DLabel items or for custom items used in + * \l{AbstractGraph3D::polar}{polar} graphs. + * + * \note: The custom item's mesh must be normalized to range \c{[-1 ,1]}, or the data + * scaling will not be accurate. + * + * \sa scaling, positionAbsolute */ /*! \qmlproperty quaternion Custom3DItem::rotation @@ -180,7 +205,9 @@ QString QCustom3DItem::meshFile() const * positionAbsolute property. When using absolute coordinates, values between \c{-1.0...1.0} are * within axis ranges. * - * \note Items positioned outside any axis range are not rendered if positionAbsolute is \c{false}. + * \note Items positioned outside any axis range are not rendered if positionAbsolute is \c{false}, + * unless the item is a QCustom3DVolume that would be partially visible. In that case, the visible + * portion of the volume will be rendered. * * \sa positionAbsolute */ @@ -211,7 +238,7 @@ void QCustom3DItem::setPositionAbsolute(bool positionAbsolute) { if (d_ptr->m_positionAbsolute != positionAbsolute) { d_ptr->m_positionAbsolute = positionAbsolute; - d_ptr->m_dirtyBits.positionAbsoluteDirty = true; + d_ptr->m_dirtyBits.positionDirty = true; emit positionAbsoluteChanged(positionAbsolute); emit d_ptr->needUpdate(); } @@ -225,8 +252,13 @@ bool QCustom3DItem::isPositionAbsolute() const /*! \property QCustom3DItem::scaling * * Holds the item \a scaling as a QVector3D. Defaults to \c {QVector3D(0.1, 0.1, 0.1)}. - * The default value sets the item to 10% of the height of the graph, provided the item size is - * normalized. + * + * Item scaling is either in data values or in absolute values, depending on the + * scalingAbsolute property. The default vector interpreted as absolute values sets the item to + * 10% of the height of the graph, provided the item mesh is normalized and the graph aspect ratios + * haven't been changed from the defaults. + * + * \sa scalingAbsolute */ void QCustom3DItem::setScaling(const QVector3D &scaling) { @@ -243,6 +275,40 @@ QVector3D QCustom3DItem::scaling() const return d_ptr->m_scaling; } +/*! \property QCustom3DItem::scalingAbsolute + * \since QtDataVisualization 1.2 + * + * This property dictates if item scaling is to be handled in data values or in absolute + * values. Defaults to \c{true}. Items with absolute scaling will be rendered at the same + * size, regardless of axis ranges. Items with data scaling will change their apparent size + * according to the axis ranges. If positionAbsolute value is \c{true}, this property is ignored + * and scaling is interpreted as an absolute value. + * + * \note: Only absolute scaling is supported for QCustom3DLabel items or for custom items used in + * \l{QAbstract3DGraph::polar}{polar} graphs. + * + * \note: The custom item's mesh must be normalized to range \c{[-1 ,1]}, or the data + * scaling will not be accurate. + * + * \sa scaling, positionAbsolute + */ +void QCustom3DItem::setScalingAbsolute(bool scalingAbsolute) +{ + if (d_ptr->m_isLabelItem && !scalingAbsolute) { + qWarning() << __FUNCTION__ << "Data bounds are not supported for label items."; + } else if (d_ptr->m_scalingAbsolute != scalingAbsolute) { + d_ptr->m_scalingAbsolute = scalingAbsolute; + d_ptr->m_dirtyBits.scalingDirty = true; + emit scalingAbsoluteChanged(scalingAbsolute); + emit d_ptr->needUpdate(); + } +} + +bool QCustom3DItem::isScalingAbsolute() const +{ + return d_ptr->m_scalingAbsolute; +} + /*! \property QCustom3DItem::rotation * * Holds the item \a rotation as a QQuaternion. Defaults to \c {QQuaternion(0.0, 0.0, 0.0, 0.0)}. @@ -371,6 +437,7 @@ QCustom3DItemPrivate::QCustom3DItemPrivate(QCustom3DItem *q) : m_position(QVector3D(0.0f, 0.0f, 0.0f)), m_positionAbsolute(false), m_scaling(QVector3D(0.1f, 0.1f, 0.1f)), + m_scalingAbsolute(true), m_rotation(QQuaternion(0.0f, 0.0f, 0.0f, 0.0f)), m_visible(true), m_shadowCasting(true), @@ -388,6 +455,7 @@ QCustom3DItemPrivate::QCustom3DItemPrivate(QCustom3DItem *q, const QString &mesh m_position(position), m_positionAbsolute(false), m_scaling(scaling), + m_scalingAbsolute(true), m_rotation(rotation), m_visible(true), m_shadowCasting(true), @@ -416,7 +484,6 @@ void QCustom3DItemPrivate::resetDirtyBits() m_dirtyBits.textureDirty = false; m_dirtyBits.meshDirty = false; m_dirtyBits.positionDirty = false; - m_dirtyBits.positionAbsoluteDirty = false; m_dirtyBits.scalingDirty = false; m_dirtyBits.rotationDirty = false; m_dirtyBits.visibleDirty = false; diff --git a/src/datavisualization/data/qcustom3ditem.h b/src/datavisualization/data/qcustom3ditem.h index 2f7f37cf..5c880213 100644 --- a/src/datavisualization/data/qcustom3ditem.h +++ b/src/datavisualization/data/qcustom3ditem.h @@ -39,6 +39,7 @@ class QT_DATAVISUALIZATION_EXPORT QCustom3DItem : public QObject Q_PROPERTY(QQuaternion rotation READ rotation WRITE setRotation NOTIFY rotationChanged) Q_PROPERTY(bool visible READ isVisible WRITE setVisible NOTIFY visibleChanged) Q_PROPERTY(bool shadowCasting READ isShadowCasting WRITE setShadowCasting NOTIFY shadowCastingChanged) + Q_PROPERTY(bool scalingAbsolute READ isScalingAbsolute WRITE setScalingAbsolute NOTIFY scalingAbsoluteChanged REVISION 1) public: explicit QCustom3DItem(QObject *parent = 0); @@ -62,6 +63,9 @@ public: void setScaling(const QVector3D &scaling); QVector3D scaling() const; + void setScalingAbsolute(bool scalingAbsolute); + bool isScalingAbsolute() const; + void setRotation(const QQuaternion &rotation); QQuaternion rotation(); @@ -84,6 +88,7 @@ signals: void rotationChanged(const QQuaternion &rotation); void visibleChanged(bool visible); void shadowCastingChanged(bool shadowCasting); + Q_REVISION(1) void scalingAbsoluteChanged(bool scalingAbsolute); protected: QCustom3DItem(QCustom3DItemPrivate *d, QObject *parent = 0); diff --git a/src/datavisualization/data/qcustom3ditem_p.h b/src/datavisualization/data/qcustom3ditem_p.h index d766bcf3..627bf53f 100644 --- a/src/datavisualization/data/qcustom3ditem_p.h +++ b/src/datavisualization/data/qcustom3ditem_p.h @@ -29,6 +29,7 @@ #ifndef QCUSTOM3DITEM_P_H #define QCUSTOM3DITEM_P_H +#include "datavisualizationglobal_p.h" #include "qcustom3ditem.h" QT_BEGIN_NAMESPACE_DATAVISUALIZATION @@ -37,7 +38,6 @@ struct QCustomItemDirtyBitField { bool textureDirty : 1; bool meshDirty : 1; bool positionDirty : 1; - bool positionAbsoluteDirty : 1; bool scalingDirty : 1; bool rotationDirty : 1; bool visibleDirty : 1; @@ -47,7 +47,6 @@ struct QCustomItemDirtyBitField { : textureDirty(false), meshDirty(false), positionDirty(false), - positionAbsoluteDirty(false), scalingDirty(false), rotationDirty(false), visibleDirty(false), @@ -77,6 +76,7 @@ public: QVector3D m_position; bool m_positionAbsolute; QVector3D m_scaling; + bool m_scalingAbsolute; QQuaternion m_rotation; bool m_visible; bool m_shadowCasting; diff --git a/src/datavisualization/data/qcustom3dvolume.cpp b/src/datavisualization/data/qcustom3dvolume.cpp index 393533c0..d0e0c139 100644 --- a/src/datavisualization/data/qcustom3dvolume.cpp +++ b/src/datavisualization/data/qcustom3dvolume.cpp @@ -623,7 +623,7 @@ void QCustom3DVolume::setSubTextureData(Qt::Axis axis, int index, const uchar *d * * \note Each X-line of the data needs to be 32bit aligned when targeting Y-axis or Z-axis. * If the textureFormat is QImage::Format_Indexed8 and textureWidth is not divisible by four, - * padding bytes need to be added to each X-line of the \a data in cases it is not already + * padding bytes need to be added to each X-line of the \a image in cases it is not already * properly aligned. The padding bytes should indicate an fully transparent color to avoid * rendering artifacts. It is not guaranteed QImage will do this automatically. * @@ -893,6 +893,8 @@ void QCustom3DVolumePrivate::resetDirtyBits() m_dirtyBitsVolume.colorTableDirty = false; m_dirtyBitsVolume.textureDataDirty = false; m_dirtyBitsVolume.textureFormatDirty = false; + m_dirtyBitsVolume.alphaDirty = false; + m_dirtyBitsVolume.shaderDirty = false; } QImage QCustom3DVolumePrivate::renderSlice(Qt::Axis axis, int index) diff --git a/src/datavisualization/engine/abstract3drenderer.cpp b/src/datavisualization/engine/abstract3drenderer.cpp index 3d5ccba1..18384872 100644 --- a/src/datavisualization/engine/abstract3drenderer.cpp +++ b/src/datavisualization/engine/abstract3drenderer.cpp @@ -960,12 +960,16 @@ CustomRenderItem *Abstract3DRenderer::addCustomItem(QCustom3DItem *item) newItem->setRenderer(this); newItem->setItemPointer(item); // Store pointer for render item updates newItem->setMesh(item->meshFile()); - QVector3D scaling = item->scaling(); + newItem->setPosition(item->position()); + newItem->setOrigScaling(item->scaling()); + newItem->setScalingAbsolute(item->isScalingAbsolute()); + newItem->setPositionAbsolute(item->isPositionAbsolute()); QImage textureImage = item->d_ptr->textureImage(); bool facingCamera = false; GLuint texture = 0; if (item->d_ptr->m_isLabelItem) { QCustom3DLabel *labelItem = static_cast(item); + newItem->setLabelItem(true); float pointSize = labelItem->font().pointSizeF(); // Check do we have custom visuals or need to use theme if (!labelItem->dptr()->m_customVisuals) { @@ -979,8 +983,10 @@ CustomRenderItem *Abstract3DRenderer::addCustomItem(QCustom3DItem *item) } // Calculate scaling based on text (texture size), font size and asked scaling float scaledFontSize = (0.05f + pointSize / 500.0f) / float(textureImage.height()); + QVector3D scaling = newItem->origScaling(); scaling.setX(scaling.x() * textureImage.width() * scaledFontSize); scaling.setY(scaling.y() * textureImage.height() * scaledFontSize); + newItem->setOrigScaling(scaling); // Check if facing camera facingCamera = labelItem->isFacingCamera(); #if !defined(QT_OPENGL_ES_2) @@ -1007,10 +1013,8 @@ CustomRenderItem *Abstract3DRenderer::addCustomItem(QCustom3DItem *item) newItem->setUseHighDefShader(volumeItem->useHighDefShader()); #endif } - newItem->setScaling(scaling); + recalculateCustomItemScaling(newItem); newItem->setRotation(item->rotation()); - newItem->setPosition(item->position()); - newItem->setPositionAbsolute(item->isPositionAbsolute()); #if !defined(QT_OPENGL_ES_2) // In OpenGL ES we simply draw volumes as regular custom item placeholders. if (!item->d_ptr->m_isVolumeItem) @@ -1021,8 +1025,8 @@ CustomRenderItem *Abstract3DRenderer::addCustomItem(QCustom3DItem *item) } newItem->setTexture(texture); item->d_ptr->clearTextureImage(); - QVector3D translation = convertPositionToTranslation(item->position(), - item->isPositionAbsolute()); + QVector3D translation = convertPositionToTranslation(newItem->position(), + newItem->isPositionAbsolute()); newItem->setTranslation(translation); newItem->setVisible(item->isVisible()); newItem->setShadowCasting(item->isShadowCasting()); @@ -1031,6 +1035,27 @@ CustomRenderItem *Abstract3DRenderer::addCustomItem(QCustom3DItem *item) return newItem; } +void Abstract3DRenderer::recalculateCustomItemScaling(CustomRenderItem *item) +{ + if (!m_polarGraph && !item->isLabel() && !item->isScalingAbsolute() + && !item->isPositionAbsolute()) { + QVector3D scale = item->origScaling() / 2.0f; + QVector3D pos = item->position(); + QVector3D minBounds(pos.x() - scale.x(), + pos.y() - scale.y(), + pos.z() - scale.z()); + QVector3D maxBounds(pos.x() + scale.x(), + pos.y() + scale.y(), + pos.z() + scale.z()); + QVector3D min = convertPositionToTranslation(minBounds, false); + QVector3D max = convertPositionToTranslation(maxBounds, false); + item->setScaling(QVector3D(qAbs(max.x() - min.x()), qAbs(max.y() - min.y()), + qAbs(max.z() - min.z())) / 2.0f); + } else { + item->setScaling(item->origScaling()); + } +} + void Abstract3DRenderer::updateCustomItem(CustomRenderItem *renderItem) { QCustom3DItem *item = renderItem->itemPointer(); @@ -1038,8 +1063,18 @@ void Abstract3DRenderer::updateCustomItem(CustomRenderItem *renderItem) renderItem->setMesh(item->meshFile()); item->d_ptr->m_dirtyBits.meshDirty = false; } + if (item->d_ptr->m_dirtyBits.positionDirty) { + renderItem->setPosition(item->position()); + renderItem->setPositionAbsolute(item->isPositionAbsolute()); + QVector3D translation = convertPositionToTranslation(renderItem->position(), + renderItem->isPositionAbsolute()); + renderItem->setTranslation(translation); + item->d_ptr->m_dirtyBits.positionDirty = false; + } if (item->d_ptr->m_dirtyBits.scalingDirty) { QVector3D scaling = item->scaling(); + renderItem->setOrigScaling(scaling); + renderItem->setScalingAbsolute(item->isScalingAbsolute()); // In case we have label item, we need to recreate texture for scaling adjustment if (item->d_ptr->m_isLabelItem) { QCustom3DLabel *labelItem = static_cast(item); @@ -1062,8 +1097,9 @@ void Abstract3DRenderer::updateCustomItem(CustomRenderItem *renderItem) scaling.setX(scaling.x() * textureImage.width() * scaledFontSize); scaling.setY(scaling.y() * textureImage.height() * scaledFontSize); item->d_ptr->clearTextureImage(); + renderItem->setOrigScaling(scaling); } - renderItem->setScaling(scaling); + recalculateCustomItemScaling(renderItem); item->d_ptr->m_dirtyBits.scalingDirty = false; } if (item->d_ptr->m_dirtyBits.rotationDirty) { @@ -1097,15 +1133,6 @@ void Abstract3DRenderer::updateCustomItem(CustomRenderItem *renderItem) item->d_ptr->clearTextureImage(); item->d_ptr->m_dirtyBits.textureDirty = false; } - if (item->d_ptr->m_dirtyBits.positionDirty || item->d_ptr->m_dirtyBits.positionAbsoluteDirty) { - renderItem->setPosition(item->position()); - renderItem->setPositionAbsolute(item->isPositionAbsolute()); - QVector3D translation = convertPositionToTranslation(item->position(), - item->isPositionAbsolute()); - renderItem->setTranslation(translation); - item->d_ptr->m_dirtyBits.positionDirty = false; - item->d_ptr->m_dirtyBits.positionAbsoluteDirty = false; - } if (item->d_ptr->m_dirtyBits.visibleDirty) { renderItem->setVisible(item->isVisible()); item->d_ptr->m_dirtyBits.visibleDirty = false; @@ -1168,6 +1195,7 @@ void Abstract3DRenderer::updateCustomItem(CustomRenderItem *renderItem) void Abstract3DRenderer::updateCustomItemPositions() { foreach (CustomRenderItem *renderItem, m_customRenderCache) { + recalculateCustomItemScaling(renderItem); QVector3D translation = convertPositionToTranslation(renderItem->position(), renderItem->isPositionAbsolute()); renderItem->setTranslation(translation); diff --git a/src/datavisualization/engine/abstract3drenderer_p.h b/src/datavisualization/engine/abstract3drenderer_p.h index 83b4e1c7..8152e0c9 100644 --- a/src/datavisualization/engine/abstract3drenderer_p.h +++ b/src/datavisualization/engine/abstract3drenderer_p.h @@ -205,6 +205,8 @@ protected: virtual void fixCameraTarget(QVector3D &target) = 0; void updateCameraViewport(); + void recalculateCustomItemScaling(CustomRenderItem *item); + bool m_hasNegativeValues; Q3DTheme *m_cachedTheme; Drawer *m_drawer; diff --git a/src/datavisualizationqml2/datavisualizationqml2_plugin.cpp b/src/datavisualizationqml2/datavisualizationqml2_plugin.cpp index c0bd4183..ecdd7454 100644 --- a/src/datavisualizationqml2/datavisualizationqml2_plugin.cpp +++ b/src/datavisualizationqml2/datavisualizationqml2_plugin.cpp @@ -115,6 +115,7 @@ void QtDataVisualizationQml2Plugin::registerTypes(const char *uri) QLatin1String("Trying to create uncreatable: AbstractGraph3D.")); qmlRegisterType(uri, 1, 2, "Surface3D"); qmlRegisterType(uri, 1, 2, "Camera3D"); + qmlRegisterType(uri, 1, 2, "Custom3DItem"); // New types qmlRegisterType(uri, 1, 2, "InputHandler3D"); diff --git a/tests/volumetrictest/main.cpp b/tests/volumetrictest/main.cpp index ba5ba6d3..02f67d6c 100644 --- a/tests/volumetrictest/main.cpp +++ b/tests/volumetrictest/main.cpp @@ -97,6 +97,27 @@ int main(int argc, char **argv) QPushButton *testSubTextureSetting = new QPushButton(widget); testSubTextureSetting->setText(QStringLiteral("Test subtexture settings")); + QLabel *rangeSliderLabel = new QLabel(QStringLiteral("Adjust ranges:"), widget); + + QSlider *rangeXSlider = new QSlider(Qt::Horizontal, widget); + rangeXSlider->setMinimum(0); + rangeXSlider->setMaximum(1024); + rangeXSlider->setValue(512); + rangeXSlider->setEnabled(true); + QSlider *rangeYSlider = new QSlider(Qt::Horizontal, widget); + rangeYSlider->setMinimum(0); + rangeYSlider->setMaximum(1024); + rangeYSlider->setValue(512); + rangeYSlider->setEnabled(true); + QSlider *rangeZSlider = new QSlider(Qt::Horizontal, widget); + rangeZSlider->setMinimum(0); + rangeZSlider->setMaximum(1024); + rangeZSlider->setValue(512); + rangeZSlider->setEnabled(true); + + QPushButton *testBoundsSetting = new QPushButton(widget); + testBoundsSetting->setText(QStringLiteral("Test data bounds")); + vLayout->addWidget(fpsLabel); vLayout->addWidget(sliceXCheckBox); vLayout->addWidget(sliceXSlider); @@ -107,6 +128,11 @@ int main(int argc, char **argv) vLayout->addWidget(sliceZCheckBox); vLayout->addWidget(sliceZSlider); vLayout->addWidget(sliceImageZLabel); + vLayout->addWidget(rangeSliderLabel); + vLayout->addWidget(rangeXSlider); + vLayout->addWidget(rangeYSlider); + vLayout->addWidget(rangeZSlider); + vLayout->addWidget(testBoundsSetting); vLayout->addWidget(testSubTextureSetting, 1, Qt::AlignTop); VolumetricModifier *modifier = new VolumetricModifier(graph); @@ -127,6 +153,14 @@ int main(int argc, char **argv) &VolumetricModifier::adjustSliceZ); QObject::connect(testSubTextureSetting, &QPushButton::clicked, modifier, &VolumetricModifier::testSubtextureSetting); + QObject::connect(rangeXSlider, &QSlider::valueChanged, modifier, + &VolumetricModifier::adjustRangeX); + QObject::connect(rangeYSlider, &QSlider::valueChanged, modifier, + &VolumetricModifier::adjustRangeY); + QObject::connect(rangeZSlider, &QSlider::valueChanged, modifier, + &VolumetricModifier::adjustRangeZ); + QObject::connect(testBoundsSetting, &QPushButton::clicked, modifier, + &VolumetricModifier::testBoundsSetting); widget->show(); return app.exec(); diff --git a/tests/volumetrictest/volumetrictest.cpp b/tests/volumetrictest/volumetrictest.cpp index 555cc286..6d7da021 100644 --- a/tests/volumetrictest/volumetrictest.cpp +++ b/tests/volumetrictest/volumetrictest.cpp @@ -31,6 +31,12 @@ using namespace QtDataVisualization; const int imageCount = 512; +const float xMiddle = 10.0f; +const float yMiddle = 12.5f; +const float zMiddle = -40.0f; +const float xRange = 40.0f; +const float yRange = 7.5f; +const float zRange = 20.0f; VolumetricModifier::VolumetricModifier(Q3DScatter *scatter) : m_graph(scatter), @@ -47,6 +53,12 @@ VolumetricModifier::VolumetricModifier(Q3DScatter *scatter) m_graph->scene()->activeCamera()->setCameraPreset(Q3DCamera::CameraPresetFront); m_graph->setOrthoProjection(true); //m_graph->scene()->activeCamera()->setTarget(QVector3D(0.5f, 0.5f, 0.5f)); + m_graph->axisX()->setRange(xMiddle - xRange, xMiddle + xRange); + m_graph->axisX()->setSegmentCount(8); + m_graph->axisY()->setRange(yMiddle - yRange, yMiddle + yRange); + m_graph->axisY()->setSegmentCount(3); + m_graph->axisZ()->setRange(zMiddle - zRange, zMiddle + zRange); + m_graph->axisZ()->setSegmentCount(8); createVolume(); createAnotherVolume(); @@ -55,6 +67,13 @@ VolumetricModifier::VolumetricModifier(Q3DScatter *scatter) // m_volumeItem->setUseHighDefShader(false); // m_volumeItem2->setUseHighDefShader(false); // m_volumeItem3->setUseHighDefShader(false); + m_volumeItem->setScalingAbsolute(false); + m_volumeItem2->setScalingAbsolute(false); + m_volumeItem3->setScalingAbsolute(false); + m_volumeItem->setPositionAbsolute(false); + m_volumeItem2->setPositionAbsolute(false); + m_volumeItem3->setPositionAbsolute(false); + m_plainItem = new QCustom3DItem; QImage texture(2, 2, QImage::Format_ARGB32); @@ -62,8 +81,9 @@ VolumetricModifier::VolumetricModifier(Q3DScatter *scatter) m_plainItem->setMeshFile(QStringLiteral(":/mesh")); m_plainItem->setTextureImage(texture); m_plainItem->setRotation(m_volumeItem->rotation()); - m_plainItem->setPosition(m_volumeItem->position() + QVector3D(0.8f, 0.0f, 0.0f)); - m_plainItem->setScaling(m_volumeItem->scaling()); + m_plainItem->setPosition(QVector3D(30.0f, 17.5f, -30.0f)); + m_plainItem->setScaling(QVector3D(20.0f, 5.0f, 10.0f)); + m_plainItem->setScalingAbsolute(false); m_graph->addCustomItem(m_volumeItem); m_graph->addCustomItem(m_volumeItem2); @@ -80,7 +100,9 @@ VolumetricModifier::VolumetricModifier(Q3DScatter *scatter) label->setScaling(QVector3D(2.0f, 2.0f, 0.0f)); label->setRotationAxisAndAngle(QVector3D(0.0f, 1.0f, 0.0f), 45.0f); label->setPosition(m_volumeItem3->position()); - label->setPositionAbsolute(true); + label->setPositionAbsolute(false); + label->setScalingAbsolute(true); + m_graph->addCustomItem(label); QObject::connect(m_graph, &QAbstract3DGraph::currentFpsChanged, this, @@ -218,6 +240,50 @@ void VolumetricModifier::testSubtextureSetting() m_volumeItem3->setSubTextureData(Qt::ZAxis, 190, slice); } +void VolumetricModifier::adjustRangeX(int value) +{ + float adjustment = float(value - 512) / 10.0f; + m_graph->axisX()->setRange(xMiddle + adjustment - xRange, xMiddle + adjustment + xRange); +} + +void VolumetricModifier::adjustRangeY(int value) +{ + float adjustment = float(value - 512) / 10.0f; + m_graph->axisY()->setRange(yMiddle + adjustment - yRange, yMiddle + adjustment + yRange); +} + +void VolumetricModifier::adjustRangeZ(int value) +{ + float adjustment = float(value - 512) / 10.0f; + m_graph->axisZ()->setRange(zMiddle + adjustment - zRange, zMiddle + adjustment + zRange); +} + +void VolumetricModifier::testBoundsSetting() +{ + static QVector3D scaling1 = m_volumeItem->scaling(); + static QVector3D scaling2 = m_volumeItem2->scaling(); + static QVector3D scaling3 = m_volumeItem3->scaling(); + static QVector3D scaleVector = QVector3D(0.5f, 0.3f, 0.2f); + + if (m_volumeItem->isScalingAbsolute()) { + m_volumeItem->setScalingAbsolute(false); + m_volumeItem2->setScalingAbsolute(false); + m_volumeItem3->setScalingAbsolute(false); + + m_volumeItem->setScaling(scaling1); + m_volumeItem2->setScaling(scaling2); + m_volumeItem3->setScaling(scaling3); + } else { + m_volumeItem->setScalingAbsolute(true); + m_volumeItem2->setScalingAbsolute(true); + m_volumeItem3->setScalingAbsolute(true); + + m_volumeItem->setScaling(scaleVector); + m_volumeItem2->setScaling(scaleVector); + m_volumeItem3->setScaling(scaleVector); + } +} + void VolumetricModifier::checkRenderCase(int id, Qt::Axis axis, int index, const QVector &dataBefore, QCustom3DVolume *volumeItem) @@ -236,7 +302,7 @@ void VolumetricModifier::createVolume() m_volumeItem = new QCustom3DVolume; m_volumeItem->setTextureFormat(QImage::Format_ARGB32); // m_volumeItem->setRotation(QQuaternion::fromAxisAndAngle(1.0f, 1.0f, 0.0f, 10.0f)); - m_volumeItem->setPosition(QVector3D(-0.5f, 0.6f, 0.0f)); + m_volumeItem->setPosition(QVector3D(xMiddle - (xRange / 2.0f), yMiddle + (yRange / 2.0f), zMiddle)); QImage logo; logo.load(QStringLiteral(":/logo_no_padding.png")); @@ -284,10 +350,7 @@ void VolumetricModifier::createVolume() int depth = m_volumeItem->textureDepth(); int frameSize = width * height; qDebug() << width << height << depth << m_volumeItem->textureData()->size(); -// m_volumeItem->setScaling(QVector3D(float(width) / float(depth) * 2.0f, -// float(height) / float(depth) * 2.0f, -// 2.0f)); - m_volumeItem->setScaling(QVector3D(0.4f, 0.4f, 0.4f)); + m_volumeItem->setScaling(QVector3D(xRange, yRange, zRange) / 2.0f); uchar *data = m_volumeItem->textureData()->data(); uchar *p = data; @@ -389,7 +452,7 @@ void VolumetricModifier::createAnotherVolume() { m_volumeItem2 = new QCustom3DVolume; m_volumeItem2->setTextureFormat(QImage::Format_ARGB32); - m_volumeItem2->setPosition(QVector3D(0.5f, -0.5f, 0.0f)); + m_volumeItem2->setPosition(QVector3D(xMiddle + (xRange / 2.0f), yMiddle - (yRange / 2.0f), zMiddle)); QImage logo; logo.load(QStringLiteral(":/logo_no_padding.png")); @@ -417,9 +480,9 @@ void VolumetricModifier::createAnotherVolume() int height = m_volumeItem2->textureHeight(); int depth = m_volumeItem2->textureDepth(); qDebug() << width << height << depth << m_volumeItem2->textureData()->size(); - m_volumeItem2->setScaling(QVector3D(float(width) / float(depth) * 2.0f, - float(height) / float(depth) * 2.0f, - 2.0f)); + m_volumeItem2->setScaling(QVector3D(float(width) / float(depth) * xRange * 2.0f, + float(height) / float(depth) * yRange * 2.0f, + zRange * 2.0f)); // Change one picture using subtexture replacement QImage flipped = logo.mirrored(); @@ -433,7 +496,7 @@ void VolumetricModifier::createYetAnotherVolume() m_volumeItem3 = new QCustom3DVolume; m_volumeItem3->setTextureFormat(QImage::Format_Indexed8); // m_volumeItem2->setRotation(QQuaternion::fromAxisAndAngle(1.0f, 1.0f, 0.0f, 10.0f)); - m_volumeItem3->setPosition(QVector3D(-0.5f, -0.6f, 0.0f)); + m_volumeItem3->setPosition(QVector3D(xMiddle - (xRange / 2.0f), yMiddle - (yRange / 2.0f), zMiddle)); // m_volumeItem3->setTextureDimensions(m_volumeItem->textureDataWidth(), // m_volumeItem->textureHeight(), diff --git a/tests/volumetrictest/volumetrictest.h b/tests/volumetrictest/volumetrictest.h index b1b98455..48c805d4 100644 --- a/tests/volumetrictest/volumetrictest.h +++ b/tests/volumetrictest/volumetrictest.h @@ -46,6 +46,10 @@ public slots: void adjustSliceZ(int value); void handleFpsChange(); void testSubtextureSetting(); + void adjustRangeX(int value); + void adjustRangeY(int value); + void adjustRangeZ(int value); + void testBoundsSetting(); private: void createVolume(); -- cgit v1.2.3 From ddb9be979d93b7e17f1067dc6056de54d9828b29 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Tue, 9 Sep 2014 11:30:15 +0300 Subject: Limit volume to axis ranges The volume object that would go partially outside axis ranges is scale and repositioned so that it only renders the portion that is inside the axis ranges. Change-Id: I792494e437998ba6276f58fab645767276c1476d Reviewed-by: Mika Salmela --- examples/datavisualization/volumetric/main.cpp | 35 +++++++- .../datavisualization/volumetric/volumetric.cpp | 53 +++++++++++- examples/datavisualization/volumetric/volumetric.h | 3 + src/datavisualization/data/customrenderitem_p.h | 26 ++++++ src/datavisualization/data/qcustom3ditem.cpp | 10 +-- src/datavisualization/data/qcustom3dvolume.cpp | 26 +++++- .../doc/src/qtdatavisualization.qdoc | 1 + .../engine/abstract3drenderer.cpp | 94 +++++++++++++++------ .../engine/abstract3drenderer_p.h | 3 +- src/datavisualization/engine/bars3drenderer.cpp | 52 ++++++++++-- src/datavisualization/engine/bars3drenderer_p.h | 1 + src/datavisualization/engine/scatter3drenderer.cpp | 40 +++++++++ src/datavisualization/engine/scatter3drenderer_p.h | 1 + .../engine/shaders/texture3d.frag | 64 +++++++------- .../engine/shaders/texture3d.vert | 26 +++++- .../engine/shaders/texture3dlowdef.frag | 64 ++++++-------- .../engine/shaders/texture3dslice.frag | 97 ++++++++++++---------- src/datavisualization/engine/surface3drenderer.cpp | 40 +++++++++ src/datavisualization/engine/surface3drenderer_p.h | 1 + .../global/datavisualizationglobal_p.h | 1 + src/datavisualization/utils/shaderhelper.cpp | 16 ++++ src/datavisualization/utils/shaderhelper_p.h | 4 + tests/volumetrictest/main.cpp | 5 +- tests/volumetrictest/volumetrictest.cpp | 90 ++++++++++++++++---- tests/volumetrictest/volumetrictest.h | 11 ++- 25 files changed, 584 insertions(+), 180 deletions(-) diff --git a/examples/datavisualization/volumetric/main.cpp b/examples/datavisualization/volumetric/main.cpp index 0acffe10..98330396 100644 --- a/examples/datavisualization/volumetric/main.cpp +++ b/examples/datavisualization/volumetric/main.cpp @@ -104,6 +104,26 @@ int main(int argc, char **argv) textureDetailVBox->addWidget(highDetailRB); textureDetailGroupBox->setLayout(textureDetailVBox); + QGroupBox *areaGroupBox = new QGroupBox(QStringLiteral("Show area")); + + QRadioButton *areaAllRB = new QRadioButton(widget); + areaAllRB->setText(QStringLiteral("Whole region")); + areaAllRB->setChecked(true); + + QRadioButton *areaMineRB = new QRadioButton(widget); + areaMineRB->setText(QStringLiteral("The mine")); + areaMineRB->setChecked(false); + + QRadioButton *areaMountainRB = new QRadioButton(widget); + areaMountainRB->setText(QStringLiteral("The mountain")); + areaMountainRB->setChecked(false); + + QVBoxLayout *areaVBox = new QVBoxLayout; + areaVBox->addWidget(areaAllRB); + areaVBox->addWidget(areaMineRB); + areaVBox->addWidget(areaMountainRB); + areaGroupBox->setLayout(areaVBox); + QCheckBox *colorTableCheckBox = new QCheckBox(widget); colorTableCheckBox->setText(QStringLiteral("Alternate color table")); colorTableCheckBox->setChecked(false); @@ -143,6 +163,11 @@ int main(int argc, char **argv) useHighDefShaderCheckBox->setText(QStringLiteral("Use HD shader")); useHighDefShaderCheckBox->setChecked(true); + QLabel *performanceNoteLabel = + new QLabel(QStringLiteral( + "Note: A high end graphics card is\nrecommended with the HD shader\nwhen the volume contains a lot of\ntransparent areas.")); + performanceNoteLabel->setFrameShape(QFrame::Box); + vLayout->addWidget(sliceXCheckBox); vLayout->addWidget(sliceXSlider); vLayout->addWidget(sliceImageXLabel); @@ -155,12 +180,14 @@ int main(int argc, char **argv) vLayout2->addWidget(fpsCheckBox); vLayout2->addWidget(fpsLabel); vLayout2->addWidget(textureDetailGroupBox); + vLayout2->addWidget(areaGroupBox); vLayout2->addWidget(colorTableCheckBox); vLayout2->addWidget(alphaMultiplierLabel); vLayout2->addWidget(alphaMultiplierSlider); vLayout2->addWidget(preserveOpacityCheckBox); vLayout2->addWidget(transparentGroundCheckBox); - vLayout2->addWidget(useHighDefShaderCheckBox, 1, Qt::AlignTop); + vLayout2->addWidget(useHighDefShaderCheckBox); + vLayout2->addWidget(performanceNoteLabel, 1, Qt::AlignTop); VolumetricModifier *modifier = new VolumetricModifier(graph); modifier->setFpsLabel(fpsLabel); @@ -201,6 +228,12 @@ int main(int argc, char **argv) &VolumetricModifier::setUseHighDefShader); QObject::connect(alphaMultiplierSlider, &QSlider::valueChanged, modifier, &VolumetricModifier::adjustAlphaMultiplier); + QObject::connect(areaAllRB, &QRadioButton::toggled, modifier, + &VolumetricModifier::toggleAreaAll); + QObject::connect(areaMineRB, &QRadioButton::toggled, modifier, + &VolumetricModifier::toggleAreaMine); + QObject::connect(areaMountainRB, &QRadioButton::toggled, modifier, + &VolumetricModifier::toggleAreaMountain); widget->show(); return app.exec(); diff --git a/examples/datavisualization/volumetric/volumetric.cpp b/examples/datavisualization/volumetric/volumetric.cpp index eb8f2188..54e930d2 100644 --- a/examples/datavisualization/volumetric/volumetric.cpp +++ b/examples/datavisualization/volumetric/volumetric.cpp @@ -80,6 +80,8 @@ VolumetricModifier::VolumetricModifier(Q3DScatter *scatter) m_graph->scene()->activeCamera()->setCameraPreset(Q3DCamera::CameraPresetFront); m_graph->setOrthoProjection(true); + toggleAreaAll(true); + #if !defined(QT_OPENGL_ES_2) m_lowDetailData = new QVector(lowDetailSize * lowDetailSize * lowDetailSize / 2); m_mediumDetailData = new QVector(mediumDetailSize * mediumDetailSize * mediumDetailSize / 2); @@ -95,9 +97,16 @@ VolumetricModifier::VolumetricModifier(Q3DScatter *scatter) excavateMineShaft(lowDetailSize, 0, m_mineShaftArray.size(), m_lowDetailData); m_volumeItem = new QCustom3DVolume; - m_volumeItem->setScaling(QVector3D(m_graph->axisX()->max() - m_graph->axisX()->min(), - m_graph->axisY()->max() - m_graph->axisY()->min(), - m_graph->axisZ()->max() - m_graph->axisZ()->min())); + // Adjust water level to zero with a minor tweak to y-coordinate position and scaling + m_volumeItem->setScaling( + QVector3D(m_graph->axisX()->max() - m_graph->axisX()->min(), + (m_graph->axisY()->max() - m_graph->axisY()->min()) * 0.91f, + m_graph->axisZ()->max() - m_graph->axisZ()->min())); + m_volumeItem->setPosition( + QVector3D((m_graph->axisX()->max() + m_graph->axisX()->min()) / 2.0f, + -0.045f * (m_graph->axisY()->max() - m_graph->axisY()->min()) + + (m_graph->axisY()->max() + m_graph->axisY()->min()) / 2.0f, + (m_graph->axisZ()->max() + m_graph->axisZ()->min()) / 2.0f)); m_volumeItem->setScalingAbsolute(false); m_volumeItem->setTextureWidth(lowDetailSize); m_volumeItem->setTextureHeight(lowDetailSize / 2); @@ -437,6 +446,42 @@ void VolumetricModifier::adjustAlphaMultiplier(int value) adjustSliceZ(m_sliceSliderZ->value()); } +void VolumetricModifier::toggleAreaAll(bool enabled) +{ + if (enabled) { + m_graph->axisX()->setRange(0.0f, 1000.0f); + m_graph->axisY()->setRange(-600.0f, 600.0f); + m_graph->axisZ()->setRange(0.0f, 1000.0f); + m_graph->axisX()->setSegmentCount(5); + m_graph->axisY()->setSegmentCount(6); + m_graph->axisZ()->setSegmentCount(5); + } +} + +void VolumetricModifier::toggleAreaMine(bool enabled) +{ + if (enabled) { + m_graph->axisX()->setRange(350.0f, 850.0f); + m_graph->axisY()->setRange(-500.0f, 100.0f); + m_graph->axisZ()->setRange(350.0f, 900.0f); + m_graph->axisX()->setSegmentCount(10); + m_graph->axisY()->setSegmentCount(6); + m_graph->axisZ()->setSegmentCount(11); + } +} + +void VolumetricModifier::toggleAreaMountain(bool enabled) +{ + if (enabled) { + m_graph->axisX()->setRange(300.0f, 600.0f); + m_graph->axisY()->setRange(-100.0f, 400.0f); + m_graph->axisZ()->setRange(300.0f, 600.0f); + m_graph->axisX()->setSegmentCount(9); + m_graph->axisY()->setSegmentCount(5); + m_graph->axisZ()->setSegmentCount(9); + } +} + void VolumetricModifier::initHeightMap(QString fileName, QVector &layerData) { QImage heightImage(fileName); @@ -485,7 +530,7 @@ int VolumetricModifier::createVolume(int textureSize, int startIndex, int count, // Ground layer below water colorIndex = int((float(waterHeights.at(k) - height) / heightToColorDiv) * float(layerColorThickness)) + underWaterGroundColorsMin; - } else if (height <= waterHeights.at(k)) { + } else if (height < waterHeights.at(k)) { // Water layer where water goes over ground colorIndex = int((float(height - magmaHeights.at(k)) / heightToColorDiv) * float(layerColorThickness)) + waterColorsMin; diff --git a/examples/datavisualization/volumetric/volumetric.h b/examples/datavisualization/volumetric/volumetric.h index 27b053e2..180686fb 100644 --- a/examples/datavisualization/volumetric/volumetric.h +++ b/examples/datavisualization/volumetric/volumetric.h @@ -63,6 +63,9 @@ public slots: void setTransparentGround(bool enabled); void setUseHighDefShader(bool enabled); void adjustAlphaMultiplier(int value); + void toggleAreaAll(bool enabled); + void toggleAreaMine(bool enabled); + void toggleAreaMountain(bool enabled); private: diff --git a/src/datavisualization/data/customrenderitem_p.h b/src/datavisualization/data/customrenderitem_p.h index 8ea8e894..84fc898a 100644 --- a/src/datavisualization/data/customrenderitem_p.h +++ b/src/datavisualization/data/customrenderitem_p.h @@ -55,6 +55,8 @@ public: inline QVector3D origScaling() const { return m_origScaling; } inline void setPosition(const QVector3D &position) { m_position = position; } inline QVector3D position() const { return m_position; } + inline void setOrigPosition(const QVector3D &position) { m_origPosition = position; } + inline QVector3D origPosition() const { return m_origPosition; } inline void setPositionAbsolute(bool absolute) { m_positionAbsolute = absolute; } inline bool isPositionAbsolute() const { return m_positionAbsolute; } inline void setScalingAbsolute(bool absolute) { m_scalingAbsolute = absolute; } @@ -104,6 +106,25 @@ public: inline bool preserveOpacity() const { return m_preserveOpacity; } inline void setUseHighDefShader(bool enable) { m_useHighDefShader = enable; } inline bool useHighDefShader() const {return m_useHighDefShader; } + inline void setMinBounds(const QVector3D &bounds) { + m_minBounds = bounds; + m_minBoundsNormal = m_minBounds; + m_minBoundsNormal.setY(-m_minBoundsNormal.y()); + m_minBoundsNormal.setZ(-m_minBoundsNormal.z()); + m_minBoundsNormal = 0.5f * (m_minBoundsNormal + oneVector); + + } + inline QVector3D minBounds() const { return m_minBounds; } + inline void setMaxBounds(const QVector3D &bounds) { + m_maxBounds = bounds; + m_maxBoundsNormal = m_maxBounds; + m_maxBoundsNormal.setY(-m_maxBoundsNormal.y()); + m_maxBoundsNormal.setZ(-m_maxBoundsNormal.z()); + m_maxBoundsNormal = 0.5f * (m_maxBoundsNormal + oneVector); + } + inline QVector3D maxBounds() const { return m_maxBounds; } + inline QVector3D minBoundsNormal() const { return m_minBoundsNormal; } + inline QVector3D maxBoundsNormal() const { return m_maxBoundsNormal; } private: Q_DISABLE_COPY(CustomRenderItem) @@ -112,6 +133,7 @@ private: QVector3D m_scaling; QVector3D m_origScaling; QVector3D m_position; + QVector3D m_origPosition; bool m_positionAbsolute; bool m_scalingAbsolute; ObjectHelper *m_object; // shared reference @@ -138,6 +160,10 @@ private: float m_alphaMultiplier; bool m_preserveOpacity; bool m_useHighDefShader; + QVector3D m_minBounds; + QVector3D m_maxBounds; + QVector3D m_minBoundsNormal; + QVector3D m_maxBoundsNormal; }; typedef QHash CustomRenderItemArray; diff --git a/src/datavisualization/data/qcustom3ditem.cpp b/src/datavisualization/data/qcustom3ditem.cpp index 64cb4531..4e82d47a 100644 --- a/src/datavisualization/data/qcustom3ditem.cpp +++ b/src/datavisualization/data/qcustom3ditem.cpp @@ -67,10 +67,10 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION * within axis ranges. * * \note Items positioned outside any axis range are not rendered if positionAbsolute is \c{false}, - * unless the item is a Custom3DVolume that would be partially visible. In that case, the visible - * portion of the volume will be rendered. + * unless the item is a Custom3DVolume that would be partially visible and scalingAbsolute is also + * \c{false}. In that case, the visible portion of the volume will be rendered. * - * \sa positionAbsolute + * \sa positionAbsolute, scalingAbsolute */ /*! \qmlproperty bool Custom3DItem::positionAbsolute @@ -206,8 +206,8 @@ QString QCustom3DItem::meshFile() const * within axis ranges. * * \note Items positioned outside any axis range are not rendered if positionAbsolute is \c{false}, - * unless the item is a QCustom3DVolume that would be partially visible. In that case, the visible - * portion of the volume will be rendered. + * unless the item is a QCustom3DVolume that would be partially visible and scalingAbsolute is also + * \c{false}. In that case, the visible portion of the volume will be rendered. * * \sa positionAbsolute */ diff --git a/src/datavisualization/data/qcustom3dvolume.cpp b/src/datavisualization/data/qcustom3dvolume.cpp index d0e0c139..78c91802 100644 --- a/src/datavisualization/data/qcustom3dvolume.cpp +++ b/src/datavisualization/data/qcustom3dvolume.cpp @@ -31,9 +31,19 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION * object is a box with a 3D texture. Three slice planes are supported for the volume, one along * each main axis of the volume. * + * Rendering volume objects is very performance intensive, especially when the volume is largely + * transparent, as the contents of the volume are ray-traced. The performance scales nearly linearly + * with the amount of pixels that the volume occupies on the screen, so showing the volume in a + * smaller view or limiting the zoom level of the graph are easy ways to improve performance. + * Similarly, the volume texture dimensions have a large impact on performance. + * If the frame rate is more important than pixel-perfect rendering of the volume contents, consider + * turning the high definition shader off by setting useHighDefShader property to \c{false}. + * * \note Volumetric objects are only supported with orthographic projection. * - * \sa QAbstract3DGraph::addCustomItem(), QAbstract3DGraph::orthoProjection + * \note Volumetric objects utilize 3D textures, which are not supported in OpenGL ES2 environments. + * + * \sa QAbstract3DGraph::addCustomItem(), QAbstract3DGraph::orthoProjection, useHighDefShader */ /*! @@ -48,13 +58,23 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION * object is a box with a 3D texture. Three slice planes are supported for the volume, one along * each main axis of the volume. * + * Rendering volume objects is very performance intensive, especially when the volume is largely + * transparent, as the contents of the volume are ray-traced. The performance scales nearly linearly + * with the amount of pixels that the volume occupies on the screen, so showing the volume in a + * smaller view or limiting the zoom level of the graph are easy ways to improve performance. + * Similarly, the volume texture dimensions have a large impact on performance. + * If the frame rate is more important than pixel-perfect rendering of the volume contents, consider + * turning the high definition shader off by setting useHighDefShader property to \c{false}. + * * \note: Filling in the volume data would not typically be efficient or practical from pure QML, * so properties directly related to that are not fully supported from QML. - * Make a hybrid QML/C++ application if you want to use volume objects with QML ui. + * Make a hybrid QML/C++ application if you want to use volume objects with a QML UI. * * \note Volumetric objects are only supported with orthographic projection. * - * \sa AbstractGraph3D::orthoProjection + * \note Volumetric objects utilize 3D textures, which are not supported in OpenGL ES2 environments. + * + * \sa AbstractGraph3D::orthoProjection, useHighDefShader */ /*! \qmlproperty int Custom3DVolume::textureWidth diff --git a/src/datavisualization/doc/src/qtdatavisualization.qdoc b/src/datavisualization/doc/src/qtdatavisualization.qdoc index a4718887..d3fb4856 100644 --- a/src/datavisualization/doc/src/qtdatavisualization.qdoc +++ b/src/datavisualization/doc/src/qtdatavisualization.qdoc @@ -327,6 +327,7 @@ so only the Qt Quick 2 graphs are available in practice for those platforms. \li Shadows are not supported with OpenGL ES2 (including Angle builds in Windows). \li Anti-aliasing doesn't work with OpenGL ES2 (including Angle builds in Windows). + \li QCustom3DVolume items are not supported with OpenGL ES2 (including Angle builds in Windows). \li Surfaces with non-straight rows and columns do not always render properly. \li Q3DLight class (and Light3D QML item) are currently not usable for anything. \li Changing any of Q3DScene properties affecting subviewports currently has no effect. diff --git a/src/datavisualization/engine/abstract3drenderer.cpp b/src/datavisualization/engine/abstract3drenderer.cpp index 18384872..305d3df0 100644 --- a/src/datavisualization/engine/abstract3drenderer.cpp +++ b/src/datavisualization/engine/abstract3drenderer.cpp @@ -960,7 +960,7 @@ CustomRenderItem *Abstract3DRenderer::addCustomItem(QCustom3DItem *item) newItem->setRenderer(this); newItem->setItemPointer(item); // Store pointer for render item updates newItem->setMesh(item->meshFile()); - newItem->setPosition(item->position()); + newItem->setOrigPosition(item->position()); newItem->setOrigScaling(item->scaling()); newItem->setScalingAbsolute(item->isScalingAbsolute()); newItem->setPositionAbsolute(item->isPositionAbsolute()); @@ -1013,7 +1013,7 @@ CustomRenderItem *Abstract3DRenderer::addCustomItem(QCustom3DItem *item) newItem->setUseHighDefShader(volumeItem->useHighDefShader()); #endif } - recalculateCustomItemScaling(newItem); + recalculateCustomItemScalingAndPos(newItem); newItem->setRotation(item->rotation()); #if !defined(QT_OPENGL_ES_2) // In OpenGL ES we simply draw volumes as regular custom item placeholders. @@ -1025,9 +1025,6 @@ CustomRenderItem *Abstract3DRenderer::addCustomItem(QCustom3DItem *item) } newItem->setTexture(texture); item->d_ptr->clearTextureImage(); - QVector3D translation = convertPositionToTranslation(newItem->position(), - newItem->isPositionAbsolute()); - newItem->setTranslation(translation); newItem->setVisible(item->isVisible()); newItem->setShadowCasting(item->isShadowCasting()); newItem->setFacingCamera(facingCamera); @@ -1035,25 +1032,71 @@ CustomRenderItem *Abstract3DRenderer::addCustomItem(QCustom3DItem *item) return newItem; } -void Abstract3DRenderer::recalculateCustomItemScaling(CustomRenderItem *item) +void Abstract3DRenderer::recalculateCustomItemScalingAndPos(CustomRenderItem *item) { if (!m_polarGraph && !item->isLabel() && !item->isScalingAbsolute() && !item->isPositionAbsolute()) { QVector3D scale = item->origScaling() / 2.0f; - QVector3D pos = item->position(); + QVector3D pos = item->origPosition(); QVector3D minBounds(pos.x() - scale.x(), pos.y() - scale.y(), - pos.z() - scale.z()); + pos.z() + scale.z()); QVector3D maxBounds(pos.x() + scale.x(), pos.y() + scale.y(), - pos.z() + scale.z()); - QVector3D min = convertPositionToTranslation(minBounds, false); - QVector3D max = convertPositionToTranslation(maxBounds, false); - item->setScaling(QVector3D(qAbs(max.x() - min.x()), qAbs(max.y() - min.y()), - qAbs(max.z() - min.z())) / 2.0f); + pos.z() - scale.z()); + QVector3D minCorner = convertPositionToTranslation(minBounds, false); + QVector3D maxCorner = convertPositionToTranslation(maxBounds, false); + scale = QVector3D(qAbs(maxCorner.x() - minCorner.x()), + qAbs(maxCorner.y() - minCorner.y()), + qAbs(maxCorner.z() - minCorner.z())) / 2.0f; + if (item->isVolume()) { + // Only volume items need to scale and reposition according to bounds + QVector3D minBoundsNormal = minCorner; + QVector3D maxBoundsNormal = maxCorner; + // getVisibleItemBounds returns bounds normalized for fragment shader [-1,1] + // Y and Z are also flipped. + getVisibleItemBounds(minBoundsNormal, maxBoundsNormal); + item->setMinBounds(minBoundsNormal); + item->setMaxBounds(maxBoundsNormal); + // For scaling calculations, we want [0,1] normalized values + minBoundsNormal = item->minBoundsNormal(); + maxBoundsNormal = item->maxBoundsNormal(); + + // Rescale and reposition the item so that it doesn't go over the edges + QVector3D adjScaling = + QVector3D(scale.x() * (maxBoundsNormal.x() - minBoundsNormal.x()), + scale.y() * (maxBoundsNormal.y() - minBoundsNormal.y()), + scale.z() * (maxBoundsNormal.z() - minBoundsNormal.z())); + + item->setScaling(adjScaling); + + QVector3D adjPos = item->origPosition(); + QVector3D dataExtents = QVector3D(maxBounds.x() - minBounds.x(), + maxBounds.y() - minBounds.y(), + maxBounds.z() - minBounds.z()) / 2.0f; + adjPos.setX(adjPos.x() + (dataExtents.x() * minBoundsNormal.x()) + - (dataExtents.x() * (1.0f - maxBoundsNormal.x()))); + adjPos.setY(adjPos.y() + (dataExtents.y() * minBoundsNormal.y()) + - (dataExtents.y() * (1.0f - maxBoundsNormal.y()))); + adjPos.setZ(adjPos.z() + (dataExtents.z() * minBoundsNormal.z()) + - (dataExtents.z() * (1.0f - maxBoundsNormal.z()))); + item->setPosition(adjPos); + } else { + // Only scale for non-volume items, and do not readjust position + item->setScaling(scale); + item->setPosition(item->origPosition()); + } } else { item->setScaling(item->origScaling()); + item->setPosition(item->origPosition()); + if (item->isVolume()) { + item->setMinBounds(-1.0f * zeroVector); + item->setMaxBounds(oneVector); + } } + QVector3D translation = convertPositionToTranslation(item->position(), + item->isPositionAbsolute()); + item->setTranslation(translation); } void Abstract3DRenderer::updateCustomItem(CustomRenderItem *renderItem) @@ -1064,11 +1107,10 @@ void Abstract3DRenderer::updateCustomItem(CustomRenderItem *renderItem) item->d_ptr->m_dirtyBits.meshDirty = false; } if (item->d_ptr->m_dirtyBits.positionDirty) { - renderItem->setPosition(item->position()); + renderItem->setOrigPosition(item->position()); renderItem->setPositionAbsolute(item->isPositionAbsolute()); - QVector3D translation = convertPositionToTranslation(renderItem->position(), - renderItem->isPositionAbsolute()); - renderItem->setTranslation(translation); + if (!item->d_ptr->m_dirtyBits.scalingDirty) + recalculateCustomItemScalingAndPos(renderItem); item->d_ptr->m_dirtyBits.positionDirty = false; } if (item->d_ptr->m_dirtyBits.scalingDirty) { @@ -1099,7 +1141,7 @@ void Abstract3DRenderer::updateCustomItem(CustomRenderItem *renderItem) item->d_ptr->clearTextureImage(); renderItem->setOrigScaling(scaling); } - recalculateCustomItemScaling(renderItem); + recalculateCustomItemScalingAndPos(renderItem); item->d_ptr->m_dirtyBits.scalingDirty = false; } if (item->d_ptr->m_dirtyBits.rotationDirty) { @@ -1195,10 +1237,7 @@ void Abstract3DRenderer::updateCustomItem(CustomRenderItem *renderItem) void Abstract3DRenderer::updateCustomItemPositions() { foreach (CustomRenderItem *renderItem, m_customRenderCache) { - recalculateCustomItemScaling(renderItem); - QVector3D translation = convertPositionToTranslation(renderItem->position(), - renderItem->isPositionAbsolute()); - renderItem->setTranslation(translation); + recalculateCustomItemScalingAndPos(renderItem); } } @@ -1243,7 +1282,7 @@ void Abstract3DRenderer::drawCustomItems(RenderingState state, continue; } - // Check if the render item is in data coordinates and not within axis ranges, and skip drawing if it is + // If the render item is in data coordinates and not within axis ranges, skip it if (!item->isPositionAbsolute() && (item->position().x() < m_axisCacheX.min() || item->position().x() > m_axisCacheX.max() @@ -1353,9 +1392,12 @@ void Abstract3DRenderer::drawCustomItems(RenderingState state, // Set shadowless shader bindings #if !defined(QT_OPENGL_ES_2) if (item->isVolume()) { - // Volume shaders repurpose light position for camera position relative to item QVector3D cameraPos = m_cachedScene->activeCamera()->position(); cameraPos = MVPMatrix.inverted().map(cameraPos); + // Adjust camera position according to min/max bounds + cameraPos = cameraPos + + ((oneVector - cameraPos) * item->minBoundsNormal()) + - ((oneVector + cameraPos) * (oneVector - item->maxBoundsNormal())); shader->setUniformValue(shader->cameraPositionRelativeToModel(), -cameraPos); GLint color8Bit = (item->textureFormat() == QImage::Format_Indexed8) ? 1 : 0; if (color8Bit) { @@ -1366,6 +1408,10 @@ void Abstract3DRenderer::drawCustomItems(RenderingState state, shader->setUniformValue(shader->alphaMultiplier(), item->alphaMultiplier()); shader->setUniformValue(shader->preserveOpacity(), item->preserveOpacity() ? 1 : 0); + + shader->setUniformValue(shader->minBounds(), item->minBounds()); + shader->setUniformValue(shader->maxBounds(), item->maxBounds()); + if (shader == m_volumeTextureSliceShader) { QVector3D slices((float(item->sliceIndexX()) + 0.5f) / float(item->textureWidth()) * 2.0 - 1.0, diff --git a/src/datavisualization/engine/abstract3drenderer_p.h b/src/datavisualization/engine/abstract3drenderer_p.h index 8152e0c9..c8bfa7af 100644 --- a/src/datavisualization/engine/abstract3drenderer_p.h +++ b/src/datavisualization/engine/abstract3drenderer_p.h @@ -205,7 +205,8 @@ protected: virtual void fixCameraTarget(QVector3D &target) = 0; void updateCameraViewport(); - void recalculateCustomItemScaling(CustomRenderItem *item); + void recalculateCustomItemScalingAndPos(CustomRenderItem *item); + virtual void getVisibleItemBounds(QVector3D &minBounds, QVector3D &maxBounds) = 0; bool m_hasNegativeValues; Q3DTheme *m_cachedTheme; diff --git a/src/datavisualization/engine/bars3drenderer.cpp b/src/datavisualization/engine/bars3drenderer.cpp index 1614b563..3e83a830 100644 --- a/src/datavisualization/engine/bars3drenderer.cpp +++ b/src/datavisualization/engine/bars3drenderer.cpp @@ -138,6 +138,46 @@ void Bars3DRenderer::fixCameraTarget(QVector3D &target) target.setZ(target.z() * -m_zScaleFactor); } +void Bars3DRenderer::getVisibleItemBounds(QVector3D &minBounds, QVector3D &maxBounds) +{ + // The inputs are the item bounds in OpenGL coordinates. + // The outputs limit these bounds to visible ranges, normalized to range [-1, 1] + // Volume shader flips the Y and Z axes, so we need to set negatives of actual values to those + float itemRangeX = (maxBounds.x() - minBounds.x()); + float itemRangeY = (maxBounds.y() - minBounds.y()); + float itemRangeZ = (maxBounds.z() - minBounds.z()); + + if (minBounds.x() < -m_xScaleFactor) + minBounds.setX(-1.0f + (2.0f * qAbs(minBounds.x() + m_xScaleFactor) / itemRangeX)); + else + minBounds.setX(-1.0f); + + if (minBounds.y() < -1.0f + m_backgroundAdjustment) + minBounds.setY(-(-1.0f + (2.0f * qAbs(minBounds.y() + 1.0f - m_backgroundAdjustment) / itemRangeY))); + else + minBounds.setY(1.0f); + + if (minBounds.z() < -m_zScaleFactor) + minBounds.setZ(-(-1.0f + (2.0f * qAbs(minBounds.z() + m_zScaleFactor) / itemRangeZ))); + else + minBounds.setZ(1.0f); + + if (maxBounds.x() > m_xScaleFactor) + maxBounds.setX(1.0f - (2.0f * qAbs(maxBounds.x() - m_xScaleFactor) / itemRangeX)); + else + maxBounds.setX(1.0f); + + if (maxBounds.y() > 1.0f + m_backgroundAdjustment) + maxBounds.setY(-(1.0f - (2.0f * qAbs(maxBounds.y() - 1.0f - m_backgroundAdjustment) / itemRangeY))); + else + maxBounds.setY(-1.0f); + + if (maxBounds.z() > m_zScaleFactor) + maxBounds.setZ(-(1.0f - (2.0f * qAbs(maxBounds.z() - m_zScaleFactor) / itemRangeZ))); + else + maxBounds.setZ(-1.0f); +} + void Bars3DRenderer::updateData() { int minRow = m_axisCacheZ.min(); @@ -1042,7 +1082,7 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) } if (m_reflectionEnabled && ((m_yFlipped && item.height() > 0.0) - || (!m_yFlipped && item.height() < 0.0))) { + || (!m_yFlipped && item.height() < 0.0))) { continue; } @@ -1564,7 +1604,7 @@ bool Bars3DRenderer::drawBars(BarRenderItem **selectedBar, && (reflection == 1.0f || (reflection != 1.0f && ((m_yFlipped && item.height() < 0.0) - || (!m_yFlipped && item.height() > 0.0))))) + || (!m_yFlipped && item.height() > 0.0))))) || !m_reflectionEnabled) { // Skip drawing of 0-height bars and reflections of bars on the "wrong side" // Set shader bindings @@ -2775,10 +2815,10 @@ QVector3D Bars3DRenderer::convertPositionToTranslation(const QVector3D &position float zTrans = 0.0f; if (!isAbsolute) { // Convert row and column to translation on graph - xTrans = (((position.x() + 0.5f) * m_cachedBarSpacing.width()) - m_rowWidth) - / m_scaleFactor; - zTrans = (m_columnDepth - ((position.z() + 0.5f) * m_cachedBarSpacing.height())) - / m_scaleFactor; + xTrans = (((position.x() - m_axisCacheX.min() + 0.5f) * m_cachedBarSpacing.width()) + - m_rowWidth) / m_scaleFactor; + zTrans = (m_columnDepth - ((position.z() - m_axisCacheZ.min() + 0.5f) + * m_cachedBarSpacing.height())) / m_scaleFactor; yTrans = m_axisCacheY.positionAt(position.y()); } else { xTrans = position.x() * m_xScaleFactor; diff --git a/src/datavisualization/engine/bars3drenderer_p.h b/src/datavisualization/engine/bars3drenderer_p.h index 44837ba2..7f1e83bb 100644 --- a/src/datavisualization/engine/bars3drenderer_p.h +++ b/src/datavisualization/engine/bars3drenderer_p.h @@ -120,6 +120,7 @@ public: protected: virtual void initializeOpenGL(); virtual void fixCameraTarget(QVector3D &target); + virtual void getVisibleItemBounds(QVector3D &minBounds, QVector3D &maxBounds); public slots: void updateMultiSeriesScaling(bool uniform); diff --git a/src/datavisualization/engine/scatter3drenderer.cpp b/src/datavisualization/engine/scatter3drenderer.cpp index ba1019ad..34386ca6 100644 --- a/src/datavisualization/engine/scatter3drenderer.cpp +++ b/src/datavisualization/engine/scatter3drenderer.cpp @@ -141,6 +141,46 @@ void Scatter3DRenderer::fixCameraTarget(QVector3D &target) target.setZ(target.z() * -m_scaleZ); } +void Scatter3DRenderer::getVisibleItemBounds(QVector3D &minBounds, QVector3D &maxBounds) +{ + // The inputs are the item bounds in OpenGL coordinates. + // The outputs limit these bounds to visible ranges, normalized to range [-1, 1] + // Volume shader flips the Y and Z axes, so we need to set negatives of actual values to those + float itemRangeX = (maxBounds.x() - minBounds.x()); + float itemRangeY = (maxBounds.y() - minBounds.y()); + float itemRangeZ = (maxBounds.z() - minBounds.z()); + + if (minBounds.x() < -m_scaleX) + minBounds.setX(-1.0f + (2.0f * qAbs(minBounds.x() + m_scaleX) / itemRangeX)); + else + minBounds.setX(-1.0f); + + if (minBounds.y() < -m_scaleY) + minBounds.setY(-(-1.0f + (2.0f * qAbs(minBounds.y() + m_scaleY) / itemRangeY))); + else + minBounds.setY(1.0f); + + if (minBounds.z() < -m_scaleZ) + minBounds.setZ(-(-1.0f + (2.0f * qAbs(minBounds.z() + m_scaleZ) / itemRangeZ))); + else + minBounds.setZ(1.0f); + + if (maxBounds.x() > m_scaleX) + maxBounds.setX(1.0f - (2.0f * qAbs(maxBounds.x() - m_scaleX) / itemRangeX)); + else + maxBounds.setX(1.0f); + + if (maxBounds.y() > m_scaleY) + maxBounds.setY(-(1.0f - (2.0f * qAbs(maxBounds.y() - m_scaleY) / itemRangeY))); + else + maxBounds.setY(-1.0f); + + if (maxBounds.z() > m_scaleZ) + maxBounds.setZ(-(1.0f - (2.0f * qAbs(maxBounds.z() - m_scaleZ) / itemRangeZ))); + else + maxBounds.setZ(-1.0f); +} + void Scatter3DRenderer::updateData() { calculateSceneScalingFactors(); diff --git a/src/datavisualization/engine/scatter3drenderer_p.h b/src/datavisualization/engine/scatter3drenderer_p.h index 3fc517d0..0852f262 100644 --- a/src/datavisualization/engine/scatter3drenderer_p.h +++ b/src/datavisualization/engine/scatter3drenderer_p.h @@ -114,6 +114,7 @@ public slots: protected: virtual void initializeOpenGL(); virtual void fixCameraTarget(QVector3D &target); + virtual void getVisibleItemBounds(QVector3D &minBounds, QVector3D &maxBounds); private: virtual void initShaders(const QString &vertexShader, const QString &fragmentShader); diff --git a/src/datavisualization/engine/shaders/texture3d.frag b/src/datavisualization/engine/shaders/texture3d.frag index 460cbcc6..3f9c42ff 100644 --- a/src/datavisualization/engine/shaders/texture3d.frag +++ b/src/datavisualization/engine/shaders/texture3d.frag @@ -1,15 +1,17 @@ #version 120 varying highp vec3 pos; +varying highp vec3 rayDir; uniform highp sampler3D textureSampler; -uniform highp vec3 cameraPositionRelativeToModel; uniform highp vec4 colorIndex[256]; uniform highp int color8Bit; uniform highp vec3 textureDimensions; uniform highp int sampleCount; // This is the maximum sample count uniform highp float alphaMultiplier; uniform highp int preserveOpacity; +uniform highp vec3 minBounds; +uniform highp vec3 maxBounds; // Ray traveling straight through a single 'alpha thickness' applies 100% of the encountered alpha. // Rays traveling shorter distances apply a fraction. This is used to normalize the alpha over @@ -17,51 +19,38 @@ uniform highp int preserveOpacity; const highp float alphaThicknesses = 32.0; void main() { - highp vec3 rayDir = -(cameraPositionRelativeToModel - pos); vec3 rayStart = pos; - // Flip Y and Z so QImage bits work directly for texture and first image is in the front - rayDir.yz = -rayDir.yz; - rayStart.yz = -rayStart.yz; - // Calculate ray intersection endpoint - vec3 rayStop; - if (rayDir.x == 0.0) { - rayStop.yz = rayStart.yz; - rayStop.x = -rayStart.x; - } else if (rayDir.y == 0.0) { - rayStop.xz = rayStart.xz; - rayStop.y = -rayStart.y; - } else if (rayDir.z == 0.0) { - rayStop.xy = rayStart.xy; - rayStop.z = -rayStart.z; - } else { - highp vec3 boxBounds = vec3(1.0, 1.0, 1.0); - highp vec3 invRayDir = 1.0 / rayDir; - if (rayDir.x < 0) - boxBounds.x = -1.0; - if (rayDir.y < 0) - boxBounds.y = -1.0; - if (rayDir.z < 0) - boxBounds.z = -1.0; - highp vec3 t = (boxBounds - rayStart) * invRayDir; - highp float minT = min(t.x, min(t.y, t.z)); - rayStop = rayStart + minT * rayDir; + highp vec3 startBounds = minBounds; + highp vec3 endBounds = maxBounds; + if (rayDir.x < 0.0) { + startBounds.x = maxBounds.x; + endBounds.x = minBounds.x; + } + if (rayDir.y > 0.0) { + startBounds.y = maxBounds.y; + endBounds.y = minBounds.y; + } + if (rayDir.z > 0.0) { + startBounds.z = maxBounds.z; + endBounds.z = minBounds.z; } + // Calculate ray intersection endpoint + highp vec3 rayStop; + highp vec3 invRayDir = 1.0 / rayDir; + highp vec3 t = (endBounds - rayStart) * invRayDir; + highp float endT = min(t.x, min(t.y, t.z)); + rayStop = rayStart + endT * rayDir; + if (endT <= 0.0) + discard; + // Convert intersections to texture coords rayStart = 0.5 * (rayStart + 1.0); rayStop = 0.5 * (rayStop + 1.0); highp vec3 ray = rayStop - rayStart; - // Avoid artifacts from divisions by zero - if (ray.x == 0) - ray.x = 0.000000001; - if (ray.y == 0) - ray.y = 0.000000001; - if (ray.z == 0) - ray.z = 0.000000001; - highp vec3 absRay = abs(ray); highp vec3 invAbsRay = 1.0 / absRay; highp float fullDist = length(ray); @@ -154,6 +143,9 @@ void main() { break; } + if (totalOpacity == 1.0) + discard; + // Brighten up the final color if there is some transparency left if (totalOpacity >= 0.0 && totalOpacity < 1.0) destColor *= (1.0 - (totalOpacity * 0.5)) / (1.0 - totalOpacity); diff --git a/src/datavisualization/engine/shaders/texture3d.vert b/src/datavisualization/engine/shaders/texture3d.vert index cad1ce06..ef3f1b25 100644 --- a/src/datavisualization/engine/shaders/texture3d.vert +++ b/src/datavisualization/engine/shaders/texture3d.vert @@ -1,12 +1,36 @@ uniform highp mat4 MVP; +uniform highp vec3 minBounds; +uniform highp vec3 maxBounds; +uniform highp vec3 cameraPositionRelativeToModel; attribute highp vec3 vertexPosition_mdl; attribute highp vec2 vertexUV; attribute highp vec3 vertexNormal_mdl; varying highp vec3 pos; +varying highp vec3 rayDir; void main() { gl_Position = MVP * vec4(vertexPosition_mdl, 1.0); - pos = vertexPosition_mdl; + + highp vec3 minBoundsNorm = minBounds; + highp vec3 maxBoundsNorm = maxBounds; + + // Y and Z are flipped in bounds to be directly usable in texture calculations, + // so flip them back to normal for position calculations + minBoundsNorm.yz = -minBoundsNorm.yz; + maxBoundsNorm.yz = -maxBoundsNorm.yz; + + minBoundsNorm = 0.5 * (minBoundsNorm + 1.0); + maxBoundsNorm = 0.5 * (maxBoundsNorm + 1.0); + + pos = vertexPosition_mdl + + ((1.0 - vertexPosition_mdl) * minBoundsNorm) + - ((1.0 + vertexPosition_mdl) * (1.0 - maxBoundsNorm)); + + rayDir = -(cameraPositionRelativeToModel - pos); + + // Flip Y and Z so QImage bits work directly for texture and first image is in the front + rayDir.yz = -rayDir.yz; + pos.yz = -pos.yz; } diff --git a/src/datavisualization/engine/shaders/texture3dlowdef.frag b/src/datavisualization/engine/shaders/texture3dlowdef.frag index 72b959fc..ed0d41ce 100644 --- a/src/datavisualization/engine/shaders/texture3dlowdef.frag +++ b/src/datavisualization/engine/shaders/texture3dlowdef.frag @@ -1,15 +1,17 @@ #version 120 varying highp vec3 pos; +varying highp vec3 rayDir; uniform highp sampler3D textureSampler; -uniform highp vec3 cameraPositionRelativeToModel; uniform highp vec4 colorIndex[256]; uniform highp int color8Bit; uniform highp vec3 textureDimensions; uniform highp int sampleCount; // This is the maximum sample count uniform highp float alphaMultiplier; uniform highp int preserveOpacity; +uniform highp vec3 minBounds; +uniform highp vec3 maxBounds; // Ray traveling straight through a single 'alpha thickness' applies 100% of the encountered alpha. // Rays traveling shorter distances apply a fraction. This is used to normalize the alpha over @@ -18,36 +20,30 @@ const highp float alphaThicknesses = 32.0; const highp float SQRT3 = 1.73205081; void main() { - highp vec3 rayDir = -(cameraPositionRelativeToModel - pos); vec3 rayStart = pos; - // Flip Y and Z so QImage bits work directly for texture and first image is in the front - rayDir.yz = -rayDir.yz; - rayStart.yz = -rayStart.yz; + highp vec3 startBounds = minBounds; + highp vec3 endBounds = maxBounds; + if (rayDir.x < 0.0) { + startBounds.x = maxBounds.x; + endBounds.x = minBounds.x; + } + if (rayDir.y > 0.0) { + startBounds.y = maxBounds.y; + endBounds.y = minBounds.y; + } + if (rayDir.z > 0.0) { + startBounds.z = maxBounds.z; + endBounds.z = minBounds.z; + } // Calculate ray intersection endpoint - vec3 rayStop; - if (rayDir.x == 0.0) { - rayStop.yz = rayStart.yz; - rayStop.x = -rayStart.x; - } else if (rayDir.y == 0.0) { - rayStop.xz = rayStart.xz; - rayStop.y = -rayStart.y; - } else if (rayDir.z == 0.0) { - rayStop.xy = rayStart.xy; - rayStop.z = -rayStart.z; - } else { - highp vec3 boxBounds = vec3(1.0, 1.0, 1.0); - highp vec3 invRayDir = 1.0 / rayDir; - if (rayDir.x < 0) - boxBounds.x = -1.0; - if (rayDir.y < 0) - boxBounds.y = -1.0; - if (rayDir.z < 0) - boxBounds.z = -1.0; - highp vec3 t = (boxBounds - rayStart) * invRayDir; - highp float minT = min(t.x, min(t.y, t.z)); - rayStop = rayStart + minT * rayDir; - } + highp vec3 rayStop; + highp vec3 invRayDir = 1.0 / rayDir; + highp vec3 t = (endBounds - rayStart) * invRayDir; + highp float endT = min(t.x, min(t.y, t.z)); + if (endT <= 0.0) + discard; + rayStop = rayStart + endT * rayDir; // Convert intersections to texture coords rayStart = 0.5 * (rayStart + 1.0); @@ -55,15 +51,6 @@ void main() { highp vec3 ray = rayStop - rayStart; - // Avoid artifacts from divisions by zero - if (ray.x == 0) - ray.x = 0.000000001; - if (ray.y == 0) - ray.y = 0.000000001; - if (ray.z == 0) - ray.z = 0.000000001; - - highp float fullDist = length(ray); highp float stepSize = SQRT3 / sampleCount; highp vec3 step = (SQRT3 * normalize(ray)) / sampleCount; @@ -110,6 +97,9 @@ void main() { break; } + if (totalOpacity == 1.0) + discard; + // Brighten up the final color if there is some transparency left if (totalOpacity >= 0.0 && totalOpacity < 1.0) destColor *= (1.0 - (totalOpacity * 0.5)) / (1.0 - totalOpacity); diff --git a/src/datavisualization/engine/shaders/texture3dslice.frag b/src/datavisualization/engine/shaders/texture3dslice.frag index 00584744..c555af98 100644 --- a/src/datavisualization/engine/shaders/texture3dslice.frag +++ b/src/datavisualization/engine/shaders/texture3dslice.frag @@ -1,14 +1,16 @@ #version 120 varying highp vec3 pos; +varying highp vec3 rayDir; uniform highp sampler3D textureSampler; -uniform highp vec3 cameraPositionRelativeToModel; uniform highp vec3 volumeSliceIndices; uniform highp vec4 colorIndex[256]; uniform highp int color8Bit; uniform highp float alphaMultiplier; uniform highp int preserveOpacity; +uniform highp vec3 minBounds; +uniform highp vec3 maxBounds; const highp vec3 xPlaneNormal = vec3(1.0, 0, 0); const highp vec3 yPlaneNormal = vec3(0, 1.0, 0); @@ -16,21 +18,17 @@ const highp vec3 zPlaneNormal = vec3(0, 0, 1.0); void main() { // Find out where ray intersects the slice planes - highp vec3 rayDir = -(cameraPositionRelativeToModel - pos); - rayDir = normalize(rayDir); + vec3 normRayDir = normalize(rayDir); highp vec3 rayStart = pos; - // Flip Y and Z so QImage bits work directly for texture and first image is in the front - rayStart.yz = -rayStart.yz; - rayDir.yz = -rayDir.yz; highp float minT = 2.0f; - if (rayDir.x != 0.0 && rayDir.y != 0.0 && rayDir.z != 0.0) { + if (normRayDir.x != 0.0 && normRayDir.y != 0.0 && normRayDir.z != 0.0) { highp vec3 boxBounds = vec3(1.0, 1.0, 1.0); - highp vec3 invRayDir = 1.0 / rayDir; - if (rayDir.x < 0) + highp vec3 invRayDir = 1.0 / normRayDir; + if (normRayDir.x < 0) boxBounds.x = -1.0; - if (rayDir.y < 0) + if (normRayDir.y < 0) boxBounds.y = -1.0; - if (rayDir.z < 0) + if (normRayDir.z < 0) boxBounds.z = -1.0; highp vec3 t = (boxBounds - rayStart) * invRayDir; minT = min(t.x, min(t.y, t.z)); @@ -43,12 +41,12 @@ void main() { highp float secondD = firstD; highp float thirdD = firstD; if (volumeSliceIndices.x >= -1.0) { - highp float dx = dot(xPoint - rayStart, xPlaneNormal) / dot(rayDir, xPlaneNormal); + highp float dx = dot(xPoint - rayStart, xPlaneNormal) / dot(normRayDir, xPlaneNormal); if (dx >= 0.0 && dx <= minT) firstD = min(dx, firstD); } if (volumeSliceIndices.y >= -1.0) { - highp float dy = dot(yPoint - rayStart, yPlaneNormal) / dot(rayDir, yPlaneNormal); + highp float dy = dot(yPoint - rayStart, yPlaneNormal) / dot(normRayDir, yPlaneNormal); if (dy >= 0.0 && dy <= minT) { if (dy < firstD) { secondD = firstD; @@ -59,7 +57,7 @@ void main() { } } if (volumeSliceIndices.z >= -1.0) { - highp float dz = dot(zPoint - rayStart, zPlaneNormal) / dot(rayDir, zPlaneNormal); + highp float dz = dot(zPoint - rayStart, zPlaneNormal) / dot(normRayDir, zPlaneNormal); if (dz >= 0.0) { if (dz < firstD && dz <= minT) { thirdD = secondD; @@ -83,43 +81,35 @@ void main() { // Convert intersection to texture coords if (firstD <= minT) { - highp vec3 firstTex = rayStart + rayDir * firstD; - firstTex = 0.5 * (firstTex + 1.0); - curColor = texture3D(textureSampler, firstTex); - if (color8Bit != 0) - curColor = colorIndex[int(curColor.r * 255.0)]; - - if (curColor.a > 0.0) { - curAlpha = curColor.a; - if (curColor.a == 1.0 && preserveOpacity != 0) - curAlpha = 1.0; - else - curAlpha = clamp(curColor.a * alphaMultiplier, 0.0, 1.0); - destColor.rgb = curColor.rgb * curAlpha; - totalAlpha = curAlpha; - } - if (secondD <= minT && totalAlpha < 1.0) { - highp vec3 secondTex = rayStart + rayDir * secondD; - secondTex = 0.5 * (secondTex + 1.0); - curColor = texture3D(textureSampler, secondTex); + highp vec3 texelVec = rayStart + normRayDir * firstD; + if (clamp(texelVec.x, minBounds.x, maxBounds.x) == texelVec.x + && clamp(texelVec.y, maxBounds.y, minBounds.y) == texelVec.y + && clamp(texelVec.z, maxBounds.z, minBounds.z) == texelVec.z) { + texelVec = 0.5 * (texelVec + 1.0); + curColor = texture3D(textureSampler, texelVec); if (color8Bit != 0) curColor = colorIndex[int(curColor.r * 255.0)]; + if (curColor.a > 0.0) { + curAlpha = curColor.a; if (curColor.a == 1.0 && preserveOpacity != 0) curAlpha = 1.0; else curAlpha = clamp(curColor.a * alphaMultiplier, 0.0, 1.0); - curRgb = curColor.rgb * curAlpha * (1.0 - totalAlpha); - destColor.rgb += curRgb; - totalAlpha += curAlpha; + destColor.rgb = curColor.rgb * curAlpha; + totalAlpha = curAlpha; } - if (thirdD <= minT && totalAlpha < 1.0) { - highp vec3 thirdTex = rayStart + rayDir * thirdD; - thirdTex = 0.5 * (thirdTex + 1.0); - curColor = texture3D(textureSampler, thirdTex); + } + if (secondD <= minT && totalAlpha < 1.0) { + texelVec = rayStart + normRayDir * secondD; + if (clamp(texelVec.x, minBounds.x, maxBounds.x) == texelVec.x + && clamp(texelVec.y, maxBounds.y, minBounds.y) == texelVec.y + && clamp(texelVec.z, maxBounds.z, minBounds.z) == texelVec.z) { + texelVec = 0.5 * (texelVec + 1.0); + curColor = texture3D(textureSampler, texelVec); + if (color8Bit != 0) + curColor = colorIndex[int(curColor.r * 255.0)]; if (curColor.a > 0.0) { - if (color8Bit != 0) - curColor = colorIndex[int(curColor.r * 255.0)]; if (curColor.a == 1.0 && preserveOpacity != 0) curAlpha = 1.0; else @@ -129,9 +119,32 @@ void main() { totalAlpha += curAlpha; } } + if (thirdD <= minT && totalAlpha < 1.0) { + texelVec = rayStart + normRayDir * thirdD; + if (clamp(texelVec.x, minBounds.x, maxBounds.x) == texelVec.x + && clamp(texelVec.y, maxBounds.y, minBounds.y) == texelVec.y + && clamp(texelVec.z, maxBounds.z, minBounds.z) == texelVec.z) { + texelVec = 0.5 * (texelVec + 1.0); + curColor = texture3D(textureSampler, texelVec); + if (curColor.a > 0.0) { + if (color8Bit != 0) + curColor = colorIndex[int(curColor.r * 255.0)]; + if (curColor.a == 1.0 && preserveOpacity != 0) + curAlpha = 1.0; + else + curAlpha = clamp(curColor.a * alphaMultiplier, 0.0, 1.0); + curRgb = curColor.rgb * curAlpha * (1.0 - totalAlpha); + destColor.rgb += curRgb; + totalAlpha += curAlpha; + } + } + } } } + if (totalAlpha == 0.0) + discard; + // Brighten up the final color if there is some transparency left if (totalAlpha > 0.0 && totalAlpha < 1.0) destColor *= 1.0 / totalAlpha; diff --git a/src/datavisualization/engine/surface3drenderer.cpp b/src/datavisualization/engine/surface3drenderer.cpp index ced4c789..fb7322cc 100644 --- a/src/datavisualization/engine/surface3drenderer.cpp +++ b/src/datavisualization/engine/surface3drenderer.cpp @@ -158,6 +158,46 @@ void Surface3DRenderer::fixCameraTarget(QVector3D &target) target.setZ(target.z() * -m_scaleZ); } +void Surface3DRenderer::getVisibleItemBounds(QVector3D &minBounds, QVector3D &maxBounds) +{ + // The inputs are the item bounds in OpenGL coordinates. + // The outputs limit these bounds to visible ranges, normalized to range [-1, 1] + // Volume shader flips the Y and Z axes, so we need to set negatives of actual values to those + float itemRangeX = (maxBounds.x() - minBounds.x()); + float itemRangeY = (maxBounds.y() - minBounds.y()); + float itemRangeZ = (maxBounds.z() - minBounds.z()); + + if (minBounds.x() < -m_scaleX) + minBounds.setX(-1.0f + (2.0f * qAbs(minBounds.x() + m_scaleX) / itemRangeX)); + else + minBounds.setX(-1.0f); + + if (minBounds.y() < -m_scaleY) + minBounds.setY(-(-1.0f + (2.0f * qAbs(minBounds.y() + m_scaleY) / itemRangeY))); + else + minBounds.setY(1.0f); + + if (minBounds.z() < -m_scaleZ) + minBounds.setZ(-(-1.0f + (2.0f * qAbs(minBounds.z() + m_scaleZ) / itemRangeZ))); + else + minBounds.setZ(1.0f); + + if (maxBounds.x() > m_scaleX) + maxBounds.setX(1.0f - (2.0f * qAbs(maxBounds.x() - m_scaleX) / itemRangeX)); + else + maxBounds.setX(1.0f); + + if (maxBounds.y() > m_scaleY) + maxBounds.setY(-(1.0f - (2.0f * qAbs(maxBounds.y() - m_scaleY) / itemRangeY))); + else + maxBounds.setY(-1.0f); + + if (maxBounds.z() > m_scaleZ) + maxBounds.setZ(-(1.0f - (2.0f * qAbs(maxBounds.z() - m_scaleZ) / itemRangeZ))); + else + maxBounds.setZ(-1.0f); +} + void Surface3DRenderer::updateData() { calculateSceneScalingFactors(); diff --git a/src/datavisualization/engine/surface3drenderer_p.h b/src/datavisualization/engine/surface3drenderer_p.h index 9c53757d..623951d4 100644 --- a/src/datavisualization/engine/surface3drenderer_p.h +++ b/src/datavisualization/engine/surface3drenderer_p.h @@ -111,6 +111,7 @@ public: protected: void initializeOpenGL(); virtual void fixCameraTarget(QVector3D &target); + virtual void getVisibleItemBounds(QVector3D &minBounds, QVector3D &maxBounds); signals: void flatShadingSupportedChanged(bool supported); diff --git a/src/datavisualization/global/datavisualizationglobal_p.h b/src/datavisualization/global/datavisualizationglobal_p.h index f1f51309..16bf7c6d 100644 --- a/src/datavisualization/global/datavisualizationglobal_p.h +++ b/src/datavisualization/global/datavisualizationglobal_p.h @@ -52,6 +52,7 @@ static const float gridLineOffset = 0.0035f; // Offset for lifting grid lines of // y position is added to the minimum height (or can be thought to be that much above or below the camera) static const QVector3D defaultLightPos = QVector3D(0.0f, 0.5f, 0.0f); static const QVector3D zeroVector = QVector3D(0.0f, 0.0f, 0.0f); +static const QVector3D oneVector = QVector3D(1.0f, 1.0f, 1.0f); static const QVector3D upVector = QVector3D(0.0f, 1.0f, 0.0f); static const QVector3D cameraDistanceVector = QVector3D(0.0f, 0.0f, cameraDistance); static const QQuaternion identityQuaternion; diff --git a/src/datavisualization/utils/shaderhelper.cpp b/src/datavisualization/utils/shaderhelper.cpp index 9d1ad0d9..b6dc1621 100644 --- a/src/datavisualization/utils/shaderhelper.cpp +++ b/src/datavisualization/utils/shaderhelper.cpp @@ -101,6 +101,8 @@ void ShaderHelper::initialize() m_sampleCountUniform = m_program->uniformLocation("sampleCount"); m_alphaMultiplierUniform = m_program->uniformLocation("alphaMultiplier"); m_preserveOpacityUniform = m_program->uniformLocation("preserveOpacity"); + m_minBoundsUniform = m_program->uniformLocation("minBounds"); + m_maxBoundsUniform = m_program->uniformLocation("maxBounds"); m_initialized = true; } @@ -324,6 +326,20 @@ GLuint ShaderHelper::preserveOpacity() return m_preserveOpacityUniform; } +GLuint ShaderHelper::maxBounds() +{ + if (!m_initialized) + qFatal("Shader not initialized"); + return m_maxBoundsUniform; +} + +GLuint ShaderHelper::minBounds() +{ + if (!m_initialized) + qFatal("Shader not initialized"); + return m_minBoundsUniform; +} + GLuint ShaderHelper::posAtt() { if (!m_initialized) diff --git a/src/datavisualization/utils/shaderhelper_p.h b/src/datavisualization/utils/shaderhelper_p.h index ac815447..853fcc8f 100644 --- a/src/datavisualization/utils/shaderhelper_p.h +++ b/src/datavisualization/utils/shaderhelper_p.h @@ -82,6 +82,8 @@ class ShaderHelper GLuint sampleCount(); GLuint alphaMultiplier(); GLuint preserveOpacity(); + GLuint maxBounds(); + GLuint minBounds(); GLuint posAtt(); GLuint uvAtt(); @@ -124,6 +126,8 @@ class ShaderHelper GLuint m_sampleCountUniform; GLuint m_alphaMultiplierUniform; GLuint m_preserveOpacityUniform; + GLuint m_minBoundsUniform; + GLuint m_maxBoundsUniform; GLboolean m_initialized; }; diff --git a/tests/volumetrictest/main.cpp b/tests/volumetrictest/main.cpp index 02f67d6c..7b18ceab 100644 --- a/tests/volumetrictest/main.cpp +++ b/tests/volumetrictest/main.cpp @@ -17,7 +17,6 @@ ****************************************************************************/ #include "volumetrictest.h" - #include #include #include @@ -32,7 +31,9 @@ int main(int argc, char **argv) { QApplication app(argc, argv); - Q3DScatter *graph = new Q3DScatter(); + //Q3DScatter *graph = new Q3DScatter(); + //Q3DSurface *graph = new Q3DSurface(); + Q3DBars *graph = new Q3DBars(); QWidget *container = QWidget::createWindowContainer(graph); QSize screenSize = graph->screen()->size(); diff --git a/tests/volumetrictest/volumetrictest.cpp b/tests/volumetrictest/volumetrictest.cpp index 6d7da021..a277c8b0 100644 --- a/tests/volumetrictest/volumetrictest.cpp +++ b/tests/volumetrictest/volumetrictest.cpp @@ -17,6 +17,7 @@ ****************************************************************************/ #include "volumetrictest.h" +#include #include #include #include @@ -31,14 +32,14 @@ using namespace QtDataVisualization; const int imageCount = 512; -const float xMiddle = 10.0f; -const float yMiddle = 12.5f; -const float zMiddle = -40.0f; +const float xMiddle = 100.0f; +const float yMiddle = 2.5f; +const float zMiddle = 100.0f; const float xRange = 40.0f; const float yRange = 7.5f; const float zRange = 20.0f; -VolumetricModifier::VolumetricModifier(Q3DScatter *scatter) +VolumetricModifier::VolumetricModifier(QAbstract3DGraph *scatter) : m_graph(scatter), m_volumeItem(0), m_volumeItem2(0), @@ -52,13 +53,55 @@ VolumetricModifier::VolumetricModifier(Q3DScatter *scatter) m_graph->setShadowQuality(QAbstract3DGraph::ShadowQualityNone); m_graph->scene()->activeCamera()->setCameraPreset(Q3DCamera::CameraPresetFront); m_graph->setOrthoProjection(true); - //m_graph->scene()->activeCamera()->setTarget(QVector3D(0.5f, 0.5f, 0.5f)); - m_graph->axisX()->setRange(xMiddle - xRange, xMiddle + xRange); - m_graph->axisX()->setSegmentCount(8); - m_graph->axisY()->setRange(yMiddle - yRange, yMiddle + yRange); - m_graph->axisY()->setSegmentCount(3); - m_graph->axisZ()->setRange(zMiddle - zRange, zMiddle + zRange); - m_graph->axisZ()->setSegmentCount(8); + //m_graph->scene()->activeCamera()->setTarget(QVector3D(-2.0f, 1.0f, 2.0f)); + m_scatterGraph = qobject_cast(m_graph); + m_surfaceGraph = qobject_cast(m_graph); + m_barGraph = qobject_cast(m_graph); + if (m_scatterGraph) { + m_scatterGraph->axisX()->setRange(xMiddle - xRange, xMiddle + xRange); + m_scatterGraph->axisX()->setSegmentCount(8); + m_scatterGraph->axisY()->setRange(yMiddle - yRange, yMiddle + yRange); + m_scatterGraph->axisY()->setSegmentCount(3); + m_scatterGraph->axisZ()->setRange(zMiddle - zRange, zMiddle + zRange); + m_scatterGraph->axisZ()->setSegmentCount(8); + } else if (m_surfaceGraph) { + m_surfaceGraph->axisX()->setRange(xMiddle - xRange, xMiddle + xRange); + m_surfaceGraph->axisX()->setSegmentCount(8); + m_surfaceGraph->axisY()->setRange(yMiddle - yRange, yMiddle + yRange); + m_surfaceGraph->axisY()->setSegmentCount(3); + m_surfaceGraph->axisZ()->setRange(zMiddle - zRange, zMiddle + zRange); + m_surfaceGraph->axisZ()->setSegmentCount(8); + } else if (m_barGraph) { + QStringList rowLabels; + QStringList columnLabels; + for (int i = 0; i < xMiddle + xRange; i++) { + if (i % 5 == 0) + columnLabels << QString::number(i); + else + columnLabels << QString(); + } + for (int i = 0; i < zMiddle + zRange; i++) { + if (i % 5 == 0) + rowLabels << QString::number(i); + else + rowLabels << QString(); + } + + QBar3DSeries *series = new QBar3DSeries; + QBarDataArray *array = new QBarDataArray(); + array->reserve(zRange * 2 + 1); + for (int i = 0; i < zRange * 2 + 1; i++) + array->append(new QBarDataRow(xRange * 2 + 1)); + + series->dataProxy()->resetArray(array, rowLabels, columnLabels); + m_barGraph->addSeries(series); + + m_barGraph->columnAxis()->setRange(xMiddle - xRange, xMiddle + xRange); + m_barGraph->valueAxis()->setRange(yMiddle - yRange, yMiddle + yRange); + m_barGraph->rowAxis()->setRange(zMiddle - zRange, zMiddle + zRange); + //m_barGraph->setReflection(true); + } + m_graph->activeTheme()->setBackgroundEnabled(false); createVolume(); createAnotherVolume(); @@ -67,6 +110,7 @@ VolumetricModifier::VolumetricModifier(Q3DScatter *scatter) // m_volumeItem->setUseHighDefShader(false); // m_volumeItem2->setUseHighDefShader(false); // m_volumeItem3->setUseHighDefShader(false); + m_volumeItem->setScalingAbsolute(false); m_volumeItem2->setScalingAbsolute(false); m_volumeItem3->setScalingAbsolute(false); @@ -81,7 +125,7 @@ VolumetricModifier::VolumetricModifier(Q3DScatter *scatter) m_plainItem->setMeshFile(QStringLiteral(":/mesh")); m_plainItem->setTextureImage(texture); m_plainItem->setRotation(m_volumeItem->rotation()); - m_plainItem->setPosition(QVector3D(30.0f, 17.5f, -30.0f)); + m_plainItem->setPosition(QVector3D(xMiddle + xRange / 2.0f, yMiddle + yRange / 2.0f, zMiddle)); m_plainItem->setScaling(QVector3D(20.0f, 5.0f, 10.0f)); m_plainItem->setScalingAbsolute(false); @@ -243,19 +287,34 @@ void VolumetricModifier::testSubtextureSetting() void VolumetricModifier::adjustRangeX(int value) { float adjustment = float(value - 512) / 10.0f; - m_graph->axisX()->setRange(xMiddle + adjustment - xRange, xMiddle + adjustment + xRange); + if (m_scatterGraph) + m_scatterGraph->axisX()->setRange(xMiddle + adjustment - xRange, xMiddle + adjustment + xRange); + if (m_surfaceGraph) + m_surfaceGraph->axisX()->setRange(xMiddle + adjustment - xRange, xMiddle + adjustment + xRange); + if (m_barGraph) + m_barGraph->columnAxis()->setRange(xMiddle + adjustment - xRange, xMiddle + adjustment + xRange); } void VolumetricModifier::adjustRangeY(int value) { float adjustment = float(value - 512) / 10.0f; - m_graph->axisY()->setRange(yMiddle + adjustment - yRange, yMiddle + adjustment + yRange); + if (m_scatterGraph) + m_scatterGraph->axisY()->setRange(yMiddle + adjustment - yRange, yMiddle + adjustment + yRange); + if (m_surfaceGraph) + m_surfaceGraph->axisY()->setRange(yMiddle + adjustment - yRange, yMiddle + adjustment + yRange); + if (m_barGraph) + m_barGraph->valueAxis()->setRange(yMiddle + adjustment - yRange, yMiddle + adjustment + yRange); } void VolumetricModifier::adjustRangeZ(int value) { float adjustment = float(value - 512) / 10.0f; - m_graph->axisZ()->setRange(zMiddle + adjustment - zRange, zMiddle + adjustment + zRange); + if (m_scatterGraph) + m_scatterGraph->axisZ()->setRange(zMiddle + adjustment - zRange, zMiddle + adjustment + zRange); + if (m_surfaceGraph) + m_surfaceGraph->axisZ()->setRange(zMiddle + adjustment - zRange, zMiddle + adjustment + zRange); + if (m_barGraph) + m_barGraph->rowAxis()->setRange(zMiddle + adjustment - zRange, zMiddle + adjustment + zRange); } void VolumetricModifier::testBoundsSetting() @@ -303,6 +362,7 @@ void VolumetricModifier::createVolume() m_volumeItem->setTextureFormat(QImage::Format_ARGB32); // m_volumeItem->setRotation(QQuaternion::fromAxisAndAngle(1.0f, 1.0f, 0.0f, 10.0f)); m_volumeItem->setPosition(QVector3D(xMiddle - (xRange / 2.0f), yMiddle + (yRange / 2.0f), zMiddle)); + //m_volumeItem->setPosition(QVector3D(xMiddle, yMiddle, zMiddle)); QImage logo; logo.load(QStringLiteral(":/logo_no_padding.png")); diff --git a/tests/volumetrictest/volumetrictest.h b/tests/volumetrictest/volumetrictest.h index 48c805d4..9029f7b9 100644 --- a/tests/volumetrictest/volumetrictest.h +++ b/tests/volumetrictest/volumetrictest.h @@ -19,9 +19,11 @@ #ifndef VOLUMETRICMODIFIER_H #define VOLUMETRICMODIFIER_H -#include #include #include +#include +#include +#include class QLabel; @@ -31,7 +33,7 @@ class VolumetricModifier : public QObject { Q_OBJECT public: - explicit VolumetricModifier(Q3DScatter *scatter); + explicit VolumetricModifier(QAbstract3DGraph *scatter); ~VolumetricModifier(); void setFpsLabel(QLabel *fpsLabel); @@ -58,7 +60,10 @@ private: void checkRenderCase(int id, Qt::Axis axis, int index, const QVector &dataBefore, QCustom3DVolume *volumeItem); - Q3DScatter *m_graph; + QAbstract3DGraph *m_graph; + Q3DScatter *m_scatterGraph; + Q3DSurface *m_surfaceGraph; + Q3DBars *m_barGraph; QCustom3DVolume *m_volumeItem; QCustom3DVolume *m_volumeItem2; QCustom3DVolume *m_volumeItem3; -- cgit v1.2.3 From e5f6ab99b413ad9b8481ad923c5a4a5bc6513ff2 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Fri, 12 Sep 2014 11:27:24 +0300 Subject: Implement volume slice frames MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I409f3c95892b26ca6097dd4509109fc9978b9900 Reviewed-by: Tomi Korpipää --- examples/datavisualization/volumetric/main.cpp | 9 +- .../datavisualization/volumetric/volumetric.cpp | 47 +++- examples/datavisualization/volumetric/volumetric.h | 5 + src/datavisualization/data/abstractrenderitem_p.h | 6 +- src/datavisualization/data/customrenderitem.cpp | 32 ++- src/datavisualization/data/customrenderitem_p.h | 78 ++++-- src/datavisualization/data/qcustom3ditem.cpp | 10 +- src/datavisualization/data/qcustom3dvolume.cpp | 303 +++++++++++++++++++-- src/datavisualization/data/qcustom3dvolume.h | 26 ++ src/datavisualization/data/qcustom3dvolume_p.h | 11 +- .../engine/abstract3drenderer.cpp | 179 ++++++++++-- .../engine/abstract3drenderer_p.h | 7 +- src/datavisualization/engine/engine.qrc | 2 + .../engine/shaders/3dsliceframes.frag | 15 + .../engine/shaders/colorandposition.vert | 10 + src/datavisualization/utils/shaderhelper.cpp | 14 + src/datavisualization/utils/shaderhelper_p.h | 3 + tests/volumetrictest/volumetrictest.cpp | 4 +- 18 files changed, 669 insertions(+), 92 deletions(-) create mode 100644 src/datavisualization/engine/shaders/3dsliceframes.frag create mode 100644 src/datavisualization/engine/shaders/colorandposition.vert diff --git a/examples/datavisualization/volumetric/main.cpp b/examples/datavisualization/volumetric/main.cpp index 98330396..faf379ec 100644 --- a/examples/datavisualization/volumetric/main.cpp +++ b/examples/datavisualization/volumetric/main.cpp @@ -168,6 +168,10 @@ int main(int argc, char **argv) "Note: A high end graphics card is\nrecommended with the HD shader\nwhen the volume contains a lot of\ntransparent areas.")); performanceNoteLabel->setFrameShape(QFrame::Box); + QCheckBox *drawSliceFramesCheckBox = new QCheckBox(widget); + drawSliceFramesCheckBox->setText(QStringLiteral("Draw slice frames")); + drawSliceFramesCheckBox->setChecked(false); + vLayout->addWidget(sliceXCheckBox); vLayout->addWidget(sliceXSlider); vLayout->addWidget(sliceImageXLabel); @@ -176,7 +180,8 @@ int main(int argc, char **argv) vLayout->addWidget(sliceImageYLabel); vLayout->addWidget(sliceZCheckBox); vLayout->addWidget(sliceZSlider); - vLayout->addWidget(sliceImageZLabel, 1, Qt::AlignTop); + vLayout->addWidget(sliceImageZLabel); + vLayout->addWidget(drawSliceFramesCheckBox, 1, Qt::AlignTop); vLayout2->addWidget(fpsCheckBox); vLayout2->addWidget(fpsLabel); vLayout2->addWidget(textureDetailGroupBox); @@ -234,6 +239,8 @@ int main(int argc, char **argv) &VolumetricModifier::toggleAreaMine); QObject::connect(areaMountainRB, &QRadioButton::toggled, modifier, &VolumetricModifier::toggleAreaMountain); + QObject::connect(drawSliceFramesCheckBox, &QCheckBox::stateChanged, modifier, + &VolumetricModifier::setDrawSliceFrames); widget->show(); return app.exec(); diff --git a/examples/datavisualization/volumetric/volumetric.cpp b/examples/datavisualization/volumetric/volumetric.cpp index 54e930d2..80df4bd5 100644 --- a/examples/datavisualization/volumetric/volumetric.cpp +++ b/examples/datavisualization/volumetric/volumetric.cpp @@ -58,6 +58,9 @@ VolumetricModifier::VolumetricModifier(Q3DScatter *scatter) m_sliceIndexX(lowDetailSize / 2), m_sliceIndexY(lowDetailSize / 4), m_sliceIndexZ(lowDetailSize / 2), + m_slicingX(false), + m_slicingY(false), + m_slicingZ(false), m_mediumDetailRB(0), m_highDetailRB(0), m_lowDetailData(0), @@ -79,6 +82,7 @@ VolumetricModifier::VolumetricModifier(Q3DScatter *scatter) m_graph->setShadowQuality(QAbstract3DGraph::ShadowQualityNone); m_graph->scene()->activeCamera()->setCameraPreset(Q3DCamera::CameraPresetFront); m_graph->setOrthoProjection(true); + m_graph->activeTheme()->setBackgroundEnabled(false); toggleAreaAll(true); @@ -162,6 +166,12 @@ VolumetricModifier::VolumetricModifier(Q3DScatter *scatter) m_volumeItem->setColorTable(m_colorTable1); + m_volumeItem->setSliceFrameGaps(QVector3D(0.01f, 0.02f, 0.01f)); + m_volumeItem->setSliceFrameThicknesses(QVector3D(0.0025f, 0.005f, 0.0025f)); + m_volumeItem->setSliceFrameWidths(QVector3D(0.0025f, 0.005f, 0.0025f)); + m_volumeItem->setDrawSliceFrames(false); + handleSlicingChanges(); + m_graph->addCustomItem(m_volumeItem); m_timer.start(0); @@ -226,20 +236,20 @@ void VolumetricModifier::setAlphaMultiplierLabel(QLabel *label) void VolumetricModifier::sliceX(int enabled) { - if (m_volumeItem) - m_volumeItem->setSliceIndexX(enabled ? m_sliceIndexX : -1); + m_slicingX = enabled; + handleSlicingChanges(); } void VolumetricModifier::sliceY(int enabled) { - if (m_volumeItem) - m_volumeItem->setSliceIndexY(enabled ? m_sliceIndexY : -1); + m_slicingY = enabled; + handleSlicingChanges(); } void VolumetricModifier::sliceZ(int enabled) { - if (m_volumeItem) - m_volumeItem->setSliceIndexZ(enabled ? m_sliceIndexZ : -1); + m_slicingZ = enabled; + handleSlicingChanges(); } void VolumetricModifier::adjustSliceX(int value) @@ -482,6 +492,12 @@ void VolumetricModifier::toggleAreaMountain(bool enabled) } } +void VolumetricModifier::setDrawSliceFrames(int enabled) +{ + if (m_volumeItem) + m_volumeItem->setDrawSliceFrames(enabled); +} + void VolumetricModifier::initHeightMap(QString fileName, QVector &layerData) { QImage heightImage(fileName); @@ -607,6 +623,25 @@ void VolumetricModifier::excavateMineBlock(int textureSize, int dataIndex, int s } } +void VolumetricModifier::handleSlicingChanges() +{ + if (m_volumeItem) { + if (m_slicingX || m_slicingY || m_slicingZ) { + // Only show slices of selected dimensions + m_volumeItem->setDrawSlices(true); + m_volumeItem->setSliceIndexX(m_slicingX ? m_sliceIndexX : -1); + m_volumeItem->setSliceIndexY(m_slicingY ? m_sliceIndexY : -1); + m_volumeItem->setSliceIndexZ(m_slicingZ ? m_sliceIndexZ : -1); + } else { + // Show slice frames for all dimenstions when not actually slicing + m_volumeItem->setDrawSlices(false); + m_volumeItem->setSliceIndexX(m_sliceIndexX); + m_volumeItem->setSliceIndexY(m_sliceIndexY); + m_volumeItem->setSliceIndexZ(m_sliceIndexZ); + } + } +} + void VolumetricModifier::initMineShaftArray() { m_mineShaftArray << QPair(QVector3D(0.7f, 0.1f, 0.7f), diff --git a/examples/datavisualization/volumetric/volumetric.h b/examples/datavisualization/volumetric/volumetric.h index 180686fb..7a4e7179 100644 --- a/examples/datavisualization/volumetric/volumetric.h +++ b/examples/datavisualization/volumetric/volumetric.h @@ -66,6 +66,7 @@ public slots: void toggleAreaAll(bool enabled); void toggleAreaMine(bool enabled); void toggleAreaMountain(bool enabled); + void setDrawSliceFrames(int enabled); private: @@ -76,12 +77,16 @@ private: int excavateMineShaft(int textureSize, int startIndex, int count, QVector *textureData); void excavateMineBlock(int textureSize, int dataIndex, int size, QVector *textureData); + void handleSlicingChanges(); Q3DScatter *m_graph; QCustom3DVolume *m_volumeItem; int m_sliceIndexX; int m_sliceIndexY; int m_sliceIndexZ; + bool m_slicingX; + bool m_slicingY; + bool m_slicingZ; QLabel *m_fpsLabel; QRadioButton *m_mediumDetailRB; QRadioButton *m_highDetailRB; diff --git a/src/datavisualization/data/abstractrenderitem_p.h b/src/datavisualization/data/abstractrenderitem_p.h index 57977a3c..ccee2b00 100644 --- a/src/datavisualization/data/abstractrenderitem_p.h +++ b/src/datavisualization/data/abstractrenderitem_p.h @@ -48,10 +48,12 @@ public: inline void setTranslation(const QVector3D &translation) { m_translation = translation; } inline const QVector3D &translation() const {return m_translation; } - inline QQuaternion rotation() const { return m_rotation; } + inline const QQuaternion &rotation() const { return m_rotation; } inline void setRotation(const QQuaternion &rotation) { - if (m_rotation != rotation) + if (rotation.isNull()) + m_rotation = identityQuaternion; + else m_rotation = rotation; } diff --git a/src/datavisualization/data/customrenderitem.cpp b/src/datavisualization/data/customrenderitem.cpp index 64194bac..f56ca86e 100644 --- a/src/datavisualization/data/customrenderitem.cpp +++ b/src/datavisualization/data/customrenderitem.cpp @@ -45,7 +45,10 @@ CustomRenderItem::CustomRenderItem() m_sliceIndexZ(-1), m_alphaMultiplier(1.0f), m_preserveOpacity(true), - m_useHighDefShader(true) + m_useHighDefShader(true), + m_drawSlices(false), + m_drawSliceFrames(false) + { } @@ -75,4 +78,31 @@ void CustomRenderItem::setColorTable(const QVector &colors) } } +void CustomRenderItem::setMinBounds(const QVector3D &bounds) +{ + m_minBounds = bounds; + m_minBoundsNormal = m_minBounds; + m_minBoundsNormal.setY(-m_minBoundsNormal.y()); + m_minBoundsNormal.setZ(-m_minBoundsNormal.z()); + m_minBoundsNormal = 0.5f * (m_minBoundsNormal + oneVector); +} + +void CustomRenderItem::setMaxBounds(const QVector3D &bounds) +{ + m_maxBounds = bounds; + m_maxBoundsNormal = m_maxBounds; + m_maxBoundsNormal.setY(-m_maxBoundsNormal.y()); + m_maxBoundsNormal.setZ(-m_maxBoundsNormal.z()); + m_maxBoundsNormal = 0.5f * (m_maxBoundsNormal + oneVector); +} + +void CustomRenderItem::setSliceFrameColor(const QColor &color) +{ + const QRgb &rgb = color.rgba(); + m_sliceFrameColor = QVector4D(float(qRed(rgb)) / 255.0f, + float(qGreen(rgb)) / 255.0f, + float(qBlue(rgb)) / 255.0f, + float(1.0f)); // Alpha not supported for frames +} + QT_END_NAMESPACE_DATAVISUALIZATION diff --git a/src/datavisualization/data/customrenderitem_p.h b/src/datavisualization/data/customrenderitem_p.h index 84fc898a..ad868fac 100644 --- a/src/datavisualization/data/customrenderitem_p.h +++ b/src/datavisualization/data/customrenderitem_p.h @@ -33,6 +33,7 @@ #include "objecthelper_p.h" #include #include +#include QT_BEGIN_NAMESPACE_DATAVISUALIZATION @@ -50,13 +51,13 @@ public: void setMesh(const QString &meshFile); inline ObjectHelper *mesh() const { return m_object; } inline void setScaling(const QVector3D &scaling) { m_scaling = scaling; } - inline QVector3D scaling() const { return m_scaling; } + inline const QVector3D &scaling() const { return m_scaling; } inline void setOrigScaling(const QVector3D &scaling) { m_origScaling = scaling; } - inline QVector3D origScaling() const { return m_origScaling; } + inline const QVector3D &origScaling() const { return m_origScaling; } inline void setPosition(const QVector3D &position) { m_position = position; } - inline QVector3D position() const { return m_position; } + inline const QVector3D &position() const { return m_position; } inline void setOrigPosition(const QVector3D &position) { m_origPosition = position; } - inline QVector3D origPosition() const { return m_origPosition; } + inline const QVector3D &origPosition() const { return m_origPosition; } inline void setPositionAbsolute(bool absolute) { m_positionAbsolute = absolute; } inline bool isPositionAbsolute() const { return m_positionAbsolute; } inline void setScalingAbsolute(bool absolute) { m_scalingAbsolute = absolute; } @@ -80,11 +81,11 @@ public: inline bool isLabel() const { return m_labelItem; } // Volume specific - inline void setTextureWidth(int width) { m_textureWidth = width; } + inline void setTextureWidth(int width) { m_textureWidth = width; setSliceIndexX(m_sliceIndexX); } inline int textureWidth() const { return m_textureWidth; } - inline void setTextureHeight(int height) { m_textureHeight = height; } + inline void setTextureHeight(int height) { m_textureHeight = height; setSliceIndexY(m_sliceIndexY); } inline int textureHeight() const { return m_textureHeight; } - inline void setTextureDepth(int depth) { m_textureDepth = depth; } + inline void setTextureDepth(int depth) { m_textureDepth = depth; setSliceIndexZ(m_sliceIndexZ); } inline int textureDepth() const { return m_textureDepth; } inline int textureSize() const { return m_textureWidth * m_textureHeight * m_textureDepth; } inline void setColorTable(const QVector &colors) { m_colorTable = colors; } @@ -94,9 +95,22 @@ public: inline bool isVolume() const { return m_isVolume; } inline void setTextureFormat(QImage::Format format) { m_textureFormat = format; } inline QImage::Format textureFormat() const { return m_textureFormat; } - inline void setSliceIndexX(int index) { m_sliceIndexX = index; } - inline void setSliceIndexY(int index) { m_sliceIndexY = index; } - inline void setSliceIndexZ(int index) { m_sliceIndexZ = index; } + inline void setSliceIndexX(int index) + { + m_sliceIndexX = index; + m_sliceFractions.setX((float(index) + 0.5f) / float(m_textureWidth) * 2.0 - 1.0); + } + inline void setSliceIndexY(int index) + { + m_sliceIndexY = index; + m_sliceFractions.setY((float(index) + 0.5f) / float(m_textureHeight) * 2.0 - 1.0); + } + inline void setSliceIndexZ(int index) + { + m_sliceIndexZ = index; + m_sliceFractions.setZ((float(index) + 0.5f) / float(m_textureDepth) * 2.0 - 1.0); + } + inline const QVector3D &sliceFractions() const { return m_sliceFractions; } inline int sliceIndexX() const { return m_sliceIndexX; } inline int sliceIndexY() const { return m_sliceIndexY; } inline int sliceIndexZ() const { return m_sliceIndexZ; } @@ -106,25 +120,24 @@ public: inline bool preserveOpacity() const { return m_preserveOpacity; } inline void setUseHighDefShader(bool enable) { m_useHighDefShader = enable; } inline bool useHighDefShader() const {return m_useHighDefShader; } - inline void setMinBounds(const QVector3D &bounds) { - m_minBounds = bounds; - m_minBoundsNormal = m_minBounds; - m_minBoundsNormal.setY(-m_minBoundsNormal.y()); - m_minBoundsNormal.setZ(-m_minBoundsNormal.z()); - m_minBoundsNormal = 0.5f * (m_minBoundsNormal + oneVector); - - } - inline QVector3D minBounds() const { return m_minBounds; } - inline void setMaxBounds(const QVector3D &bounds) { - m_maxBounds = bounds; - m_maxBoundsNormal = m_maxBounds; - m_maxBoundsNormal.setY(-m_maxBoundsNormal.y()); - m_maxBoundsNormal.setZ(-m_maxBoundsNormal.z()); - m_maxBoundsNormal = 0.5f * (m_maxBoundsNormal + oneVector); - } - inline QVector3D maxBounds() const { return m_maxBounds; } - inline QVector3D minBoundsNormal() const { return m_minBoundsNormal; } - inline QVector3D maxBoundsNormal() const { return m_maxBoundsNormal; } + void setMinBounds(const QVector3D &bounds); + inline const QVector3D &minBounds() const { return m_minBounds; } + void setMaxBounds(const QVector3D &bounds); + inline const QVector3D &maxBounds() const { return m_maxBounds; } + inline const QVector3D &minBoundsNormal() const { return m_minBoundsNormal; } + inline const QVector3D &maxBoundsNormal() const { return m_maxBoundsNormal; } + inline void setDrawSlices(bool enable) { m_drawSlices = enable; } + inline bool drawSlices() const {return m_drawSlices; } + inline void setDrawSliceFrames(bool enable) { m_drawSliceFrames = enable; } + inline bool drawSliceFrames() const {return m_drawSliceFrames; } + void setSliceFrameColor(const QColor &color); + inline const QVector4D &sliceFrameColor() const { return m_sliceFrameColor; } + inline void setSliceFrameWidths(const QVector3D &widths) { m_sliceFrameWidths = widths * 2.0f; } + inline const QVector3D &sliceFrameWidths() const { return m_sliceFrameWidths; } + inline void setSliceFrameGaps(const QVector3D &gaps) { m_sliceFrameGaps = gaps * 2.0f; } + inline const QVector3D &sliceFrameGaps() const { return m_sliceFrameGaps; } + inline void setSliceFrameThicknesses(const QVector3D &thicknesses) { m_sliceFrameThicknesses = thicknesses; } + inline const QVector3D &sliceFrameThicknesses() const { return m_sliceFrameThicknesses; } private: Q_DISABLE_COPY(CustomRenderItem) @@ -157,6 +170,7 @@ private: int m_sliceIndexX; int m_sliceIndexY; int m_sliceIndexZ; + QVector3D m_sliceFractions; float m_alphaMultiplier; bool m_preserveOpacity; bool m_useHighDefShader; @@ -164,6 +178,12 @@ private: QVector3D m_maxBounds; QVector3D m_minBoundsNormal; QVector3D m_maxBoundsNormal; + bool m_drawSlices; + bool m_drawSliceFrames; + QVector4D m_sliceFrameColor; + QVector3D m_sliceFrameWidths; + QVector3D m_sliceFrameGaps; + QVector3D m_sliceFrameThicknesses; }; typedef QHash CustomRenderItemArray; diff --git a/src/datavisualization/data/qcustom3ditem.cpp b/src/datavisualization/data/qcustom3ditem.cpp index 4e82d47a..b9825732 100644 --- a/src/datavisualization/data/qcustom3ditem.cpp +++ b/src/datavisualization/data/qcustom3ditem.cpp @@ -101,7 +101,9 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION * values. Defaults to \c{true}. Items with absolute scaling will be rendered at the same * size, regardless of axis ranges. Items with data scaling will change their apparent size * according to the axis ranges. If positionAbsolute value is \c{true}, this property is ignored - * and scaling is interpreted as an absolute value. + * and scaling is interpreted as an absolute value. If the item has rotation, the data scaling + * is calculated on the unrotated item. Similarly, for Custom3DVolume items, the range clipping + * is calculated on the unrotated item. * * \note: Only absolute scaling is supported for Custom3DLabel items or for custom items used in * \l{AbstractGraph3D::polar}{polar} graphs. @@ -282,7 +284,9 @@ QVector3D QCustom3DItem::scaling() const * values. Defaults to \c{true}. Items with absolute scaling will be rendered at the same * size, regardless of axis ranges. Items with data scaling will change their apparent size * according to the axis ranges. If positionAbsolute value is \c{true}, this property is ignored - * and scaling is interpreted as an absolute value. + * and scaling is interpreted as an absolute value. If the item has rotation, the data scaling + * is calculated on the unrotated item. Similarly, for QCustom3DVolume items, the range clipping + * is calculated on the unrotated item. * * \note: Only absolute scaling is supported for QCustom3DLabel items or for custom items used in * \l{QAbstract3DGraph::polar}{polar} graphs. @@ -438,7 +442,7 @@ QCustom3DItemPrivate::QCustom3DItemPrivate(QCustom3DItem *q) : m_positionAbsolute(false), m_scaling(QVector3D(0.1f, 0.1f, 0.1f)), m_scalingAbsolute(true), - m_rotation(QQuaternion(0.0f, 0.0f, 0.0f, 0.0f)), + m_rotation(identityQuaternion), m_visible(true), m_shadowCasting(true), m_isLabelItem(false), diff --git a/src/datavisualization/data/qcustom3dvolume.cpp b/src/datavisualization/data/qcustom3dvolume.cpp index 78c91802..563af31a 100644 --- a/src/datavisualization/data/qcustom3dvolume.cpp +++ b/src/datavisualization/data/qcustom3dvolume.cpp @@ -104,31 +104,34 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION /*! \qmlproperty int Custom3DVolume::sliceIndexX * * The X dimension index into the texture data indicating which vertical slice to show. - * Setting any dimension to negative indicates no slice for that dimension is drawn. - * If all dimensions are negative, no slices are drawn and the volume is drawn normally. + * Setting any dimension to negative indicates no slice or slice frame for that dimension is drawn. + * If all dimensions are negative, no slices or slice frames are drawn and the volume is drawn + * normally. * Defaults to \c{-1}. * - * \sa QCustom3DVolume::textureData + * \sa QCustom3DVolume::textureData, drawSlices, drawSliceFrames */ /*! \qmlproperty int Custom3DVolume::sliceIndexY * * The Y dimension index into the texture data indicating which horizontal slice to show. - * Setting any dimension to negative indicates no slice for that dimension is drawn. - * If all dimensions are negative, no slices are drawn and the volume is drawn normally. + * Setting any dimension to negative indicates no slice or slice frame for that dimension is drawn. + * If all dimensions are negative, no slices or slice frames are drawn and the volume is drawn + * normally. * Defaults to \c{-1}. * - * \sa QCustom3DVolume::textureData + * \sa QCustom3DVolume::textureData, drawSlices, drawSliceFrames */ /*! \qmlproperty int Custom3DVolume::sliceIndexZ * * The Z dimension index into the texture data indicating which vertical slice to show. - * Setting any dimension to negative indicates no slice for that dimension is drawn. - * If all dimensions are negative, no slices are drawn and the volume is drawn normally. + * Setting any dimension to negative indicates no slice or slice frame for that dimension is drawn. + * If all dimensions are negative, no slices or slice frames are drawn and the volume is drawn + * normally. * Defaults to \c{-1}. * - * \sa QCustom3DVolume::textureData + * \sa QCustom3DVolume::textureData, drawSlices, drawSliceFrames */ /*! @@ -173,6 +176,79 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION * Defaults to \c{true}. */ +/*! + * \qmlproperty bool Custom3DVolume::drawSlices + * + * If this property value is \c{true}, the slices indicated by slice index properties + * will be drawn instead of the full volume. + * If it is \c{false}, the full volume will always be drawn. + * Defaults to \c{false}. + * + * \note The slices are always drawn along the item axes, so if the item is rotated, the slices are + * rotated as well. + * + * \sa sliceIndexX, sliceIndexY, sliceIndexZ + */ + +/*! + * \qmlproperty bool Custom3DVolume::drawSliceFrames + * + * If this property value is \c{true}, the frames of slices indicated by slice index properties + * will be drawn around the volume. + * If it is \c{false}, no slice frames will be drawn. + * Drawing slice frames is independent of drawing slices, so you can show the full volume and + * still draw the slice frames around it. + * Defaults to \c{false}. + * + * \sa sliceIndexX, sliceIndexY, sliceIndexZ, drawSlices + */ + +/*! + * \qmlproperty color Custom3DVolume::sliceFrameColor + * + * Indicates the color of the slice frame. Transparent slice frame color is not supported. + * + * Defaults to black. + * + * \sa drawSliceFrames + */ + +/*! + * \qmlproperty vector3d Custom3DVolume::sliceFrameWidths + * + * Indicates the widths of the slice frame. The width can be different on different dimensions, + * so you can for example omit drawing the frames on certain sides of the volume by setting the + * value for that dimension to zero. The values are fractions of the volume thickness in the same + * dimension. The values cannot be negative. + * + * Defaults to \c{vector3d(0.01, 0.01, 0.01)}. + * + * \sa drawSliceFrames + */ + +/*! + * \qmlproperty vector3d Custom3DVolume::sliceFrameGaps + * + * Indicates the amount of air gap left between the volume itself and the frame in each dimension. + * The gap can be different on different dimensions. The values are fractions of the volume + * thickness in the same dimension. The values cannot be negative. + * + * Defaults to \c{vector3d(0.01, 0.01, 0.01)}. + * + * \sa drawSliceFrames + */ + +/*! + * \qmlproperty vector3d Custom3DVolume::sliceFrameThicknesses + * + * Indicates the thickness of the slice frames for each dimension. The values are fractions of + * the volume thickness in the same dimension. The values cannot be negative. + * + * Defaults to \c{vector3d(0.01, 0.01, 0.01)}. + * + * \sa drawSliceFrames + */ + /*! * Constructs QCustom3DVolume with given \a parent. */ @@ -324,17 +400,18 @@ int QCustom3DVolume::textureDataWidth() const /*! \property QCustom3DVolume::sliceIndexX * * The X dimension index into the texture data indicating which vertical slice to show. - * Setting any dimension to negative indicates no slice for that dimension is drawn. - * If all dimensions are negative, no slices are drawn and the volume is drawn normally. + * Setting any dimension to negative indicates no slice or slice frame for that dimension is drawn. + * If all dimensions are negative, no slices or slice frames are drawn and the volume is drawn + * normally. * Defaults to \c{-1}. * - * \sa textureData + * \sa textureData, drawSlices, drawSliceFrames */ void QCustom3DVolume::setSliceIndexX(int value) { if (dptr()->m_sliceIndexX != value) { dptr()->m_sliceIndexX = value; - dptr()->m_dirtyBitsVolume.sliceIndicesDirty = true; + dptr()->m_dirtyBitsVolume.slicesDirty = true; emit sliceIndexXChanged(value); emit dptr()->needUpdate(); } @@ -348,17 +425,18 @@ int QCustom3DVolume::sliceIndexX() const /*! \property QCustom3DVolume::sliceIndexY * * The Y dimension index into the texture data indicating which horizontal slice to show. - * Setting any dimension to negative indicates no slice for that dimension is drawn. - * If all dimensions are negative, no slices are drawn and the volume is drawn normally. + * Setting any dimension to negative indicates no slice or slice frame for that dimension is drawn. + * If all dimensions are negative, no slices or slice frames are drawn and the volume is drawn + * normally. * Defaults to \c{-1}. * - * \sa textureData + * \sa textureData, drawSlices, drawSliceFrames */ void QCustom3DVolume::setSliceIndexY(int value) { if (dptr()->m_sliceIndexY != value) { dptr()->m_sliceIndexY = value; - dptr()->m_dirtyBitsVolume.sliceIndicesDirty = true; + dptr()->m_dirtyBitsVolume.slicesDirty = true; emit sliceIndexYChanged(value); emit dptr()->needUpdate(); } @@ -372,17 +450,18 @@ int QCustom3DVolume::sliceIndexY() const /*! \property QCustom3DVolume::sliceIndexZ * * The Z dimension index into the texture data indicating which vertical slice to show. - * Setting any dimension to negative indicates no slice for that dimension is drawn. - * If all dimensions are negative, no slices are drawn and the volume is drawn normally. + * Setting any dimension to negative indicates no slice or slice frame for that dimension is drawn. + * If all dimensions are negative, no slices or slice frames are drawn and the volume is drawn + * normally. * Defaults to \c{-1}. * - * \sa textureData + * \sa textureData, drawSlices, drawSliceFrames */ void QCustom3DVolume::setSliceIndexZ(int value) { if (dptr()->m_sliceIndexZ != value) { dptr()->m_sliceIndexZ = value; - dptr()->m_dirtyBitsVolume.sliceIndicesDirty = true; + dptr()->m_dirtyBitsVolume.slicesDirty = true; emit sliceIndexZChanged(value); emit dptr()->needUpdate(); } @@ -816,6 +895,170 @@ bool QCustom3DVolume::useHighDefShader() const return dptrc()->m_useHighDefShader; } +/*! + * \property QCustom3DVolume::drawSlices + * + * If this property value is \c{true}, the slices indicated by slice index properties + * will be drawn instead of the full volume. + * If it is \c{false}, the full volume will always be drawn. + * Defaults to \c{false}. + * + * \note The slices are always drawn along the item axes, so if the item is rotated, the slices are + * rotated as well. + * + * \sa sliceIndexX, sliceIndexY, sliceIndexZ + */ +void QCustom3DVolume::setDrawSlices(bool enable) +{ + if (dptr()->m_drawSlices != enable) { + dptr()->m_drawSlices = enable; + dptr()->m_dirtyBitsVolume.slicesDirty = true; + emit drawSlicesChanged(enable); + emit dptr()->needUpdate(); + } +} + +bool QCustom3DVolume::drawSlices() const +{ + return dptrc()->m_drawSlices; +} + +/*! + * \property QCustom3DVolume::drawSliceFrames + * + * If this property value is \c{true}, the frames of slices indicated by slice index properties + * will be drawn around the volume. + * If it is \c{false}, no slice frames will be drawn. + * Drawing slice frames is independent of drawing slices, so you can show the full volume and + * still draw the slice frames around it. This is useful when using renderSlice() to display the + * slices outside the graph itself. + * Defaults to \c{false}. + * + * \sa sliceIndexX, sliceIndexY, sliceIndexZ, drawSlices, renderSlice() + */ +void QCustom3DVolume::setDrawSliceFrames(bool enable) +{ + if (dptr()->m_drawSliceFrames != enable) { + dptr()->m_drawSliceFrames = enable; + dptr()->m_dirtyBitsVolume.slicesDirty = true; + emit drawSliceFramesChanged(enable); + emit dptr()->needUpdate(); + } +} + +bool QCustom3DVolume::drawSliceFrames() const +{ + return dptrc()->m_drawSliceFrames; +} + +/*! + * \property QCustom3DVolume::sliceFrameColor + * + * Indicates the color of the slice frame. Transparent slice frame color is not supported. + * + * Defaults to black. + * + * \sa drawSliceFrames + */ +void QCustom3DVolume::setSliceFrameColor(const QColor &color) +{ + if (dptr()->m_sliceFrameColor != color) { + dptr()->m_sliceFrameColor = color; + dptr()->m_dirtyBitsVolume.slicesDirty = true; + emit sliceFrameColorChanged(color); + emit dptr()->needUpdate(); + } +} + +QColor QCustom3DVolume::sliceFrameColor() const +{ + return dptrc()->m_sliceFrameColor; +} + +/*! + * \property QCustom3DVolume::sliceFrameWidths + * + * Indicates the widths of the slice frame. The width can be different on different dimensions, + * so you can for example omit drawing the frames on certain sides of the volume by setting the + * value for that dimension to zero. The values are fractions of the volume thickness in the same + * dimension. The values cannot be negative. + * + * Defaults to \c{QVector3D(0.01, 0.01, 0.01)}. + * + * \sa drawSliceFrames + */ +void QCustom3DVolume::setSliceFrameWidths(const QVector3D &values) +{ + if (values.x() < 0.0f || values.y() < 0.0f || values.z() < 0.0f) { + qWarning() << __FUNCTION__ << "Attempted to set negative values."; + } else if (dptr()->m_sliceFrameWidths != values) { + dptr()->m_sliceFrameWidths = values; + dptr()->m_dirtyBitsVolume.slicesDirty = true; + emit sliceFrameWidthsChanged(values); + emit dptr()->needUpdate(); + } +} + +QVector3D QCustom3DVolume::sliceFrameWidths() const +{ + return dptrc()->m_sliceFrameWidths; +} + +/*! + * \property QCustom3DVolume::sliceFrameGaps + * + * Indicates the amount of air gap left between the volume itself and the frame in each dimension. + * The gap can be different on different dimensions. The values are fractions of the volume + * thickness in the same dimension. The values cannot be negative. + * + * Defaults to \c{QVector3D(0.01, 0.01, 0.01)}. + * + * \sa drawSliceFrames + */ +void QCustom3DVolume::setSliceFrameGaps(const QVector3D &values) +{ + if (values.x() < 0.0f || values.y() < 0.0f || values.z() < 0.0f) { + qWarning() << __FUNCTION__ << "Attempted to set negative values."; + } else if (dptr()->m_sliceFrameGaps != values) { + dptr()->m_sliceFrameGaps = values; + dptr()->m_dirtyBitsVolume.slicesDirty = true; + emit sliceFrameGapsChanged(values); + emit dptr()->needUpdate(); + } +} + +QVector3D QCustom3DVolume::sliceFrameGaps() const +{ + return dptrc()->m_sliceFrameGaps; +} + +/*! + * \property QCustom3DVolume::sliceFrameThicknesses + * + * Indicates the thickness of the slice frames for each dimension. The values are fractions of + * the volume thickness in the same dimension. The values cannot be negative. + * + * Defaults to \c{QVector3D(0.01, 0.01, 0.01)}. + * + * \sa drawSliceFrames + */ +void QCustom3DVolume::setSliceFrameThicknesses(const QVector3D &values) +{ + if (values.x() < 0.0f || values.y() < 0.0f || values.z() < 0.0f) { + qWarning() << __FUNCTION__ << "Attempted to set negative values."; + } else if (dptr()->m_sliceFrameThicknesses != values) { + dptr()->m_sliceFrameThicknesses = values; + dptr()->m_dirtyBitsVolume.slicesDirty = true; + emit sliceFrameThicknessesChanged(values); + emit dptr()->needUpdate(); + } +} + +QVector3D QCustom3DVolume::sliceFrameThicknesses() const +{ + return dptrc()->m_sliceFrameThicknesses; +} + /*! * Renders the slice specified by \a index along \a axis into an image. * The texture format of this object is used. @@ -857,7 +1100,13 @@ QCustom3DVolumePrivate::QCustom3DVolumePrivate(QCustom3DVolume *q) : m_textureData(0), m_alphaMultiplier(1.0f), m_preserveOpacity(true), - m_useHighDefShader(true) + m_useHighDefShader(true), + m_drawSlices(false), + m_drawSliceFrames(false), + m_sliceFrameColor(Qt::black), + m_sliceFrameWidths(QVector3D(0.01f, 0.01f, 0.01f)), + m_sliceFrameGaps(QVector3D(0.01f, 0.01f, 0.01f)), + m_sliceFrameThicknesses(QVector3D(0.01f, 0.01f, 0.01f)) { m_isVolumeItem = true; m_meshFile = QStringLiteral(":/defaultMeshes/barFull"); @@ -882,7 +1131,13 @@ QCustom3DVolumePrivate::QCustom3DVolumePrivate(QCustom3DVolume *q, const QVector m_textureData(textureData), m_alphaMultiplier(1.0f), m_preserveOpacity(true), - m_useHighDefShader(true) + m_useHighDefShader(true), + m_drawSlices(false), + m_drawSliceFrames(false), + m_sliceFrameColor(Qt::black), + m_sliceFrameWidths(QVector3D(0.01f, 0.01f, 0.01f)), + m_sliceFrameGaps(QVector3D(0.01f, 0.01f, 0.01f)), + m_sliceFrameThicknesses(QVector3D(0.01f, 0.01f, 0.01f)) { m_isVolumeItem = true; m_shadowCasting = false; @@ -909,7 +1164,7 @@ void QCustom3DVolumePrivate::resetDirtyBits() QCustom3DItemPrivate::resetDirtyBits(); m_dirtyBitsVolume.textureDimensionsDirty = false; - m_dirtyBitsVolume.sliceIndicesDirty = false; + m_dirtyBitsVolume.slicesDirty = false; m_dirtyBitsVolume.colorTableDirty = false; m_dirtyBitsVolume.textureDataDirty = false; m_dirtyBitsVolume.textureFormatDirty = false; diff --git a/src/datavisualization/data/qcustom3dvolume.h b/src/datavisualization/data/qcustom3dvolume.h index 04434263..a9dfda86 100644 --- a/src/datavisualization/data/qcustom3dvolume.h +++ b/src/datavisualization/data/qcustom3dvolume.h @@ -42,6 +42,12 @@ class QT_DATAVISUALIZATION_EXPORT QCustom3DVolume : public QCustom3DItem Q_PROPERTY(float alphaMultiplier READ alphaMultiplier WRITE setAlphaMultiplier NOTIFY alphaMultiplierChanged) Q_PROPERTY(bool preserveOpacity READ preserveOpacity WRITE setPreserveOpacity NOTIFY preserveOpacityChanged) Q_PROPERTY(bool useHighDefShader READ useHighDefShader WRITE setUseHighDefShader NOTIFY useHighDefShaderChanged) + Q_PROPERTY(bool drawSlices READ drawSlices WRITE setDrawSlices NOTIFY drawSlicesChanged) + Q_PROPERTY(bool drawSliceFrames READ drawSliceFrames WRITE setDrawSliceFrames NOTIFY drawSliceFramesChanged) + Q_PROPERTY(QColor sliceFrameColor READ sliceFrameColor WRITE setSliceFrameColor NOTIFY sliceFrameColorChanged) + Q_PROPERTY(QVector3D sliceFrameWidths READ sliceFrameWidths WRITE setSliceFrameWidths NOTIFY sliceFrameWidthsChanged) + Q_PROPERTY(QVector3D sliceFrameGaps READ sliceFrameGaps WRITE setSliceFrameGaps NOTIFY sliceFrameGapsChanged) + Q_PROPERTY(QVector3D sliceFrameThicknesses READ sliceFrameThicknesses WRITE setSliceFrameThicknesses NOTIFY sliceFrameThicknessesChanged) public: @@ -90,6 +96,20 @@ public: void setUseHighDefShader(bool enable); bool useHighDefShader() const; + void setDrawSlices(bool enable); + bool drawSlices() const; + void setDrawSliceFrames(bool enable); + bool drawSliceFrames() const; + + void setSliceFrameColor(const QColor &color); + QColor sliceFrameColor() const; + void setSliceFrameWidths(const QVector3D &values); + QVector3D sliceFrameWidths() const; + void setSliceFrameGaps(const QVector3D &values); + QVector3D sliceFrameGaps() const; + void setSliceFrameThicknesses(const QVector3D &values); + QVector3D sliceFrameThicknesses() const; + QImage renderSlice(Qt::Axis axis, int index); signals: @@ -105,6 +125,12 @@ signals: void alphaMultiplierChanged(float mult); void preserveOpacityChanged(bool enabled); void useHighDefShaderChanged(bool enabled); + void drawSlicesChanged(bool enabled); + void drawSliceFramesChanged(bool enabled); + void sliceFrameColorChanged(const QColor &color); + void sliceFrameWidthsChanged(const QVector3D &values); + void sliceFrameGapsChanged(const QVector3D &values); + void sliceFrameThicknessesChanged(const QVector3D &values); protected: QCustom3DVolumePrivate *dptr(); diff --git a/src/datavisualization/data/qcustom3dvolume_p.h b/src/datavisualization/data/qcustom3dvolume_p.h index 8b0b439e..642d2af5 100644 --- a/src/datavisualization/data/qcustom3dvolume_p.h +++ b/src/datavisualization/data/qcustom3dvolume_p.h @@ -36,7 +36,7 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION struct QCustomVolumeDirtyBitField { bool textureDimensionsDirty : 1; - bool sliceIndicesDirty : 1; + bool slicesDirty : 1; bool colorTableDirty : 1; bool textureDataDirty : 1; bool textureFormatDirty : 1; @@ -45,7 +45,7 @@ struct QCustomVolumeDirtyBitField { QCustomVolumeDirtyBitField() : textureDimensionsDirty(false), - sliceIndicesDirty(false), + slicesDirty(false), colorTableDirty(false), textureDataDirty(false), textureFormatDirty(false), @@ -88,6 +88,13 @@ public: bool m_preserveOpacity; bool m_useHighDefShader; + bool m_drawSlices; + bool m_drawSliceFrames; + QColor m_sliceFrameColor; + QVector3D m_sliceFrameWidths; + QVector3D m_sliceFrameGaps; + QVector3D m_sliceFrameThicknesses; + QCustomVolumeDirtyBitField m_dirtyBitsVolume; private: diff --git a/src/datavisualization/engine/abstract3drenderer.cpp b/src/datavisualization/engine/abstract3drenderer.cpp index 305d3df0..4dd58490 100644 --- a/src/datavisualization/engine/abstract3drenderer.cpp +++ b/src/datavisualization/engine/abstract3drenderer.cpp @@ -62,6 +62,7 @@ Abstract3DRenderer::Abstract3DRenderer(Abstract3DController *controller) m_volumeTextureShader(0), m_volumeTextureLowDefShader(0), m_volumeTextureSliceShader(0), + m_volumeSliceFrameShader(0), m_useOrthoProjection(false), m_xFlipped(false), m_yFlipped(false), @@ -105,6 +106,7 @@ Abstract3DRenderer::~Abstract3DRenderer() delete m_customItemShader; delete m_volumeTextureShader; delete m_volumeTextureLowDefShader; + delete m_volumeSliceFrameShader; delete m_volumeTextureSliceShader; foreach (SeriesRenderCache *cache, m_renderCacheList) { @@ -208,7 +210,9 @@ void Abstract3DRenderer::initCustomItemShaders(const QString &vertexShader, void Abstract3DRenderer::initVolumeTextureShaders(const QString &vertexShader, const QString &fragmentShader, const QString &fragmentLowDefShader, - const QString &sliceShader) + const QString &sliceShader, + const QString &sliceFrameVertexShader, + const QString &sliceFrameShader) { delete m_volumeTextureShader; @@ -216,13 +220,16 @@ void Abstract3DRenderer::initVolumeTextureShaders(const QString &vertexShader, m_volumeTextureShader->initialize(); delete m_volumeTextureLowDefShader; - m_volumeTextureLowDefShader = new ShaderHelper(this, vertexShader, - fragmentLowDefShader); + m_volumeTextureLowDefShader = new ShaderHelper(this, vertexShader, fragmentLowDefShader); m_volumeTextureLowDefShader->initialize(); delete m_volumeTextureSliceShader; m_volumeTextureSliceShader = new ShaderHelper(this, vertexShader, sliceShader); m_volumeTextureSliceShader->initialize(); + + delete m_volumeSliceFrameShader; + m_volumeSliceFrameShader = new ShaderHelper(this, sliceFrameVertexShader, sliceFrameShader); + m_volumeSliceFrameShader->initialize(); } void Abstract3DRenderer::updateTheme(Q3DTheme *theme) @@ -327,7 +334,9 @@ void Abstract3DRenderer::reInitShaders() initVolumeTextureShaders(QStringLiteral(":/shaders/vertexTexture3D"), QStringLiteral(":/shaders/fragmentTexture3D"), QStringLiteral(":/shaders/fragmentTexture3DLowDef"), - QStringLiteral(":/shaders/fragmentTexture3DSlice")); + QStringLiteral(":/shaders/fragmentTexture3DSlice"), + QStringLiteral(":/shaders/colorAndPosition"), + QStringLiteral(":/shaders/fragment3DSliceFrames")); #else if (m_cachedOptimizationHint.testFlag(QAbstract3DGraph::OptimizationStatic) && qobject_cast(this)) { @@ -1011,6 +1020,13 @@ CustomRenderItem *Abstract3DRenderer::addCustomItem(QCustom3DItem *item) newItem->setAlphaMultiplier(volumeItem->alphaMultiplier()); newItem->setPreserveOpacity(volumeItem->preserveOpacity()); newItem->setUseHighDefShader(volumeItem->useHighDefShader()); + + newItem->setDrawSlices(volumeItem->drawSlices()); + newItem->setDrawSliceFrames(volumeItem->drawSliceFrames()); + newItem->setSliceFrameColor(volumeItem->sliceFrameColor()); + newItem->setSliceFrameWidths(volumeItem->sliceFrameWidths()); + newItem->setSliceFrameGaps(volumeItem->sliceFrameGaps()); + newItem->setSliceFrameThicknesses(volumeItem->sliceFrameThicknesses()); #endif } recalculateCustomItemScalingAndPos(newItem); @@ -1215,11 +1231,17 @@ void Abstract3DRenderer::updateCustomItem(CustomRenderItem *renderItem) volumeItem->dptr()->m_dirtyBitsVolume.textureDataDirty = false; volumeItem->dptr()->m_dirtyBitsVolume.textureFormatDirty = false; } - if (volumeItem->dptr()->m_dirtyBitsVolume.sliceIndicesDirty) { + if (volumeItem->dptr()->m_dirtyBitsVolume.slicesDirty) { + renderItem->setDrawSlices(volumeItem->drawSlices()); + renderItem->setDrawSliceFrames(volumeItem->drawSliceFrames()); + renderItem->setSliceFrameColor(volumeItem->sliceFrameColor()); + renderItem->setSliceFrameWidths(volumeItem->sliceFrameWidths()); + renderItem->setSliceFrameGaps(volumeItem->sliceFrameGaps()); + renderItem->setSliceFrameThicknesses(volumeItem->sliceFrameThicknesses()); renderItem->setSliceIndexX(volumeItem->sliceIndexX()); renderItem->setSliceIndexY(volumeItem->sliceIndexY()); renderItem->setSliceIndexZ(volumeItem->sliceIndexZ()); - volumeItem->dptr()->m_dirtyBitsVolume.sliceIndicesDirty = false; + volumeItem->dptr()->m_dirtyBitsVolume.slicesDirty = false; } if (volumeItem->dptr()->m_dirtyBitsVolume.alphaDirty) { renderItem->setAlphaMultiplier(volumeItem->alphaMultiplier()); @@ -1236,9 +1258,8 @@ void Abstract3DRenderer::updateCustomItem(CustomRenderItem *renderItem) void Abstract3DRenderer::updateCustomItemPositions() { - foreach (CustomRenderItem *renderItem, m_customRenderCache) { + foreach (CustomRenderItem *renderItem, m_customRenderCache) recalculateCustomItemScalingAndPos(renderItem); - } } void Abstract3DRenderer::drawCustomItems(RenderingState state, @@ -1345,9 +1366,10 @@ void Abstract3DRenderer::drawCustomItems(RenderingState state, #if !defined(QT_OPENGL_ES_2) ShaderHelper *prevShader = shader; if (item->isVolume()) { - if (item->sliceIndexX() >= 0 - || item->sliceIndexY() >= 0 - || item->sliceIndexZ() >= 0) { + if (item->drawSlices() && + (item->sliceIndexX() >= 0 + || item->sliceIndexY() >= 0 + || item->sliceIndexZ() >= 0)) { shader = m_volumeTextureSliceShader; } else if (item->useHighDefShader()) { shader = m_volumeTextureShader; @@ -1395,10 +1417,10 @@ void Abstract3DRenderer::drawCustomItems(RenderingState state, QVector3D cameraPos = m_cachedScene->activeCamera()->position(); cameraPos = MVPMatrix.inverted().map(cameraPos); // Adjust camera position according to min/max bounds - cameraPos = cameraPos - + ((oneVector - cameraPos) * item->minBoundsNormal()) - - ((oneVector + cameraPos) * (oneVector - item->maxBoundsNormal())); - shader->setUniformValue(shader->cameraPositionRelativeToModel(), -cameraPos); + cameraPos = -(cameraPos + + ((oneVector - cameraPos) * item->minBoundsNormal()) + - ((oneVector + cameraPos) * (oneVector - item->maxBoundsNormal()))); + shader->setUniformValue(shader->cameraPositionRelativeToModel(), cameraPos); GLint color8Bit = (item->textureFormat() == QImage::Format_Indexed8) ? 1 : 0; if (color8Bit) { shader->setUniformValueArray(shader->colorIndex(), @@ -1412,14 +1434,10 @@ void Abstract3DRenderer::drawCustomItems(RenderingState state, shader->setUniformValue(shader->minBounds(), item->minBounds()); shader->setUniformValue(shader->maxBounds(), item->maxBounds()); + QVector3D slices; if (shader == m_volumeTextureSliceShader) { - QVector3D slices((float(item->sliceIndexX()) + 0.5f) - / float(item->textureWidth()) * 2.0 - 1.0, - (float(item->sliceIndexY()) + 0.5f) - / float(item->textureHeight()) * 2.0 - 1.0, - (float(item->sliceIndexZ()) + 0.5f) - / float(item->textureDepth()) * 2.0 - 1.0); - shader->setUniformValue(shader->volumeSliceIndices(), slices); + shader->setUniformValue(shader->volumeSliceIndices(), + item->sliceFractions()); } else { // Precalculate texture dimensions so we can optimize // ray stepping to hit every texture layer. @@ -1443,6 +1461,24 @@ void Abstract3DRenderer::drawCustomItems(RenderingState state, shader->setUniformValue(shader->textureDimensions(), textureDimensions); shader->setUniformValue(shader->sampleCount(), sampleCount); } + if (item->drawSliceFrames()) { + // Set up the slice frame shader + glDisable(GL_CULL_FACE); + m_volumeSliceFrameShader->bind(); + m_volumeSliceFrameShader->setUniformValue( + m_volumeSliceFrameShader->color(), item->sliceFrameColor()); + + // Draw individual slice frames. + if (item->sliceIndexX() >= 0) + drawVolumeSliceFrame(item, Qt::XAxis, projectionViewMatrix); + if (item->sliceIndexY() >= 0) + drawVolumeSliceFrame(item, Qt::YAxis, projectionViewMatrix); + if (item->sliceIndexZ() >= 0) + drawVolumeSliceFrame(item, Qt::ZAxis, projectionViewMatrix); + + glEnable(GL_CULL_FACE); + shader->bind(); + } m_drawer->drawObject(shader, item->mesh(), 0, 0, item->texture()); } else #endif @@ -1476,6 +1512,105 @@ void Abstract3DRenderer::drawCustomItems(RenderingState state, } } +void Abstract3DRenderer::drawVolumeSliceFrame(const CustomRenderItem *item, Qt::Axis axis, + const QMatrix4x4 &projectionViewMatrix) +{ + QVector2D frameWidth; + QVector3D frameScaling; + QVector3D translation = item->translation(); + QQuaternion rotation = item->rotation(); + float fracTrans; + bool needRotate = !rotation.isIdentity(); + QMatrix4x4 rotationMatrix; + if (needRotate) + rotationMatrix.rotate(rotation); + + if (axis == Qt::XAxis) { + fracTrans = item->sliceFractions().x(); + float range = item->maxBoundsNormal().x() - item->minBoundsNormal().x(); + float minMult = item->minBoundsNormal().x() / range; + float maxMult = (1.0f - item->maxBoundsNormal().x()) / range; + fracTrans = fracTrans - ((1.0f - fracTrans) * minMult) + ((1.0f + fracTrans) * maxMult); + if (needRotate) + translation -= rotationMatrix.map(QVector3D(fracTrans * item->scaling().x(), 0.0f, 0.0f)); + else + translation.setX(translation.x() + fracTrans * item->scaling().x()); + frameScaling = QVector3D(item->scaling().z() + + (item->scaling().z() * item->sliceFrameGaps().z()) + + (item->scaling().z() * item->sliceFrameWidths().z()), + item->scaling().y() + + (item->scaling().y() * item->sliceFrameGaps().y()) + + (item->scaling().y() * item->sliceFrameWidths().y()), + item->scaling().x() * item->sliceFrameThicknesses().x()); + frameWidth = QVector2D(item->scaling().z() * item->sliceFrameWidths().z(), + item->scaling().y() * item->sliceFrameWidths().y()); + rotation *= m_yRightAngleRotation; + } else if (axis == Qt::YAxis) { + fracTrans = item->sliceFractions().y(); + float range = item->maxBoundsNormal().y() - item->minBoundsNormal().y(); + // Y axis is logically flipped, so we need to swam min and max bounds + float minMult = (1.0f - item->maxBoundsNormal().y()) / range; + float maxMult = item->minBoundsNormal().y() / range; + fracTrans = fracTrans - ((1.0f - fracTrans) * minMult) + ((1.0f + fracTrans) * maxMult); + if (needRotate) + translation -= rotationMatrix.map(QVector3D(0.0f, fracTrans * item->scaling().y(), 0.0f)); + else + translation.setY(translation.y() - fracTrans * item->scaling().y()); + frameScaling = QVector3D(item->scaling().x() + + (item->scaling().x() * item->sliceFrameGaps().x()) + + (item->scaling().x() * item->sliceFrameWidths().x()), + item->scaling().z() + + (item->scaling().z() * item->sliceFrameGaps().z()) + + (item->scaling().z() * item->sliceFrameWidths().z()), + item->scaling().y() * item->sliceFrameThicknesses().y()); + frameWidth = QVector2D(item->scaling().x() * item->sliceFrameWidths().x(), + item->scaling().z() * item->sliceFrameWidths().z()); + rotation *= m_xRightAngleRotation; + } else { // Z axis + fracTrans = item->sliceFractions().z(); + float range = item->maxBoundsNormal().z() - item->minBoundsNormal().z(); + // Z axis is logically flipped, so we need to swam min and max bounds + float minMult = (1.0f - item->maxBoundsNormal().z()) / range; + float maxMult = item->minBoundsNormal().z() / range; + fracTrans = fracTrans - ((1.0f - fracTrans) * minMult) + ((1.0f + fracTrans) * maxMult); + if (needRotate) + translation -= rotationMatrix.map(QVector3D(0.0f, 0.0f, fracTrans * item->scaling().z())); + else + translation.setZ(translation.z() - fracTrans * item->scaling().z()); + frameScaling = QVector3D(item->scaling().x() + + (item->scaling().x() * item->sliceFrameGaps().x()) + + (item->scaling().x() * item->sliceFrameWidths().x()), + item->scaling().y() + + (item->scaling().y() * item->sliceFrameGaps().y()) + + (item->scaling().y() * item->sliceFrameWidths().y()), + item->scaling().z() * item->sliceFrameThicknesses().z()); + frameWidth = QVector2D(item->scaling().x() * item->sliceFrameWidths().x(), + item->scaling().y() * item->sliceFrameWidths().y()); + } + + // If the slice is outside the shown area, don't show the frame + if (fracTrans < -1.0 || fracTrans > 1.0) + return; + + // Shader needs the width of clear space in the middle. + frameWidth.setX(1.0f - (frameWidth.x() / frameScaling.x())); + frameWidth.setY(1.0f - (frameWidth.y() / frameScaling.y())); + + QMatrix4x4 modelMatrix; + QMatrix4x4 mvpMatrix; + + modelMatrix.translate(translation); + modelMatrix.rotate(rotation); + modelMatrix.scale(frameScaling); + mvpMatrix = projectionViewMatrix * modelMatrix; + m_volumeSliceFrameShader->setUniformValue(m_volumeSliceFrameShader->MVP(), mvpMatrix); + m_volumeSliceFrameShader->setUniformValue(m_volumeSliceFrameShader->sliceFrameWidth(), + frameWidth); + + m_drawer->drawObject(m_volumeSliceFrameShader, item->mesh()); + +} + void Abstract3DRenderer::calculatePolarXZ(const QVector3D &dataPos, float &x, float &z) const { // x is angular, z is radial diff --git a/src/datavisualization/engine/abstract3drenderer_p.h b/src/datavisualization/engine/abstract3drenderer_p.h index c8bfa7af..b31cfbd3 100644 --- a/src/datavisualization/engine/abstract3drenderer_p.h +++ b/src/datavisualization/engine/abstract3drenderer_p.h @@ -95,7 +95,9 @@ public: virtual void initVolumeTextureShaders(const QString &vertexShader, const QString &fragmentShader, const QString &fragmentLowDefShader, - const QString &sliceShader); + const QString &sliceShader, + const QString &sliceFrameVertexShader, + const QString &sliceFrameShader); virtual void updateAxisType(QAbstract3DAxis::AxisOrientation orientation, QAbstract3DAxis::AxisType type); virtual void updateAxisTitle(QAbstract3DAxis::AxisOrientation orientation, @@ -207,6 +209,8 @@ protected: void recalculateCustomItemScalingAndPos(CustomRenderItem *item); virtual void getVisibleItemBounds(QVector3D &minBounds, QVector3D &maxBounds) = 0; + void drawVolumeSliceFrame(const CustomRenderItem *item, Qt::Axis axis, + const QMatrix4x4 &projectionViewMatrix); bool m_hasNegativeValues; Q3DTheme *m_cachedTheme; @@ -248,6 +252,7 @@ protected: ShaderHelper *m_volumeTextureShader; ShaderHelper *m_volumeTextureLowDefShader; ShaderHelper *m_volumeTextureSliceShader; + ShaderHelper *m_volumeSliceFrameShader; bool m_useOrthoProjection; bool m_xFlipped; diff --git a/src/datavisualization/engine/engine.qrc b/src/datavisualization/engine/engine.qrc index eeddf92e..af759ba3 100644 --- a/src/datavisualization/engine/engine.qrc +++ b/src/datavisualization/engine/engine.qrc @@ -66,5 +66,7 @@ shaders/defaultNoMatrices.vert shaders/texture3dlowdef.frag shaders/point_ES2_UV.vert + shaders/3dsliceframes.frag + shaders/colorandposition.vert diff --git a/src/datavisualization/engine/shaders/3dsliceframes.frag b/src/datavisualization/engine/shaders/3dsliceframes.frag new file mode 100644 index 00000000..44c080dc --- /dev/null +++ b/src/datavisualization/engine/shaders/3dsliceframes.frag @@ -0,0 +1,15 @@ +#version 120 + +uniform highp vec4 color_mdl; +uniform highp vec2 sliceFrameWidth; + +varying highp vec3 pos; + +void main() { + highp vec2 absPos = min(vec2(1.0, 1.0), abs(pos.xy)); + if (absPos.x > sliceFrameWidth.x || absPos.y > sliceFrameWidth.y) + gl_FragColor = color_mdl; + else + discard; +} + diff --git a/src/datavisualization/engine/shaders/colorandposition.vert b/src/datavisualization/engine/shaders/colorandposition.vert new file mode 100644 index 00000000..34849eae --- /dev/null +++ b/src/datavisualization/engine/shaders/colorandposition.vert @@ -0,0 +1,10 @@ +uniform highp mat4 MVP; + +attribute highp vec3 vertexPosition_mdl; + +varying highp vec3 pos; + +void main() { + gl_Position = MVP * vec4(vertexPosition_mdl, 1.0); + pos = vertexPosition_mdl; +} diff --git a/src/datavisualization/utils/shaderhelper.cpp b/src/datavisualization/utils/shaderhelper.cpp index b6dc1621..aa43c2df 100644 --- a/src/datavisualization/utils/shaderhelper.cpp +++ b/src/datavisualization/utils/shaderhelper.cpp @@ -103,6 +103,7 @@ void ShaderHelper::initialize() m_preserveOpacityUniform = m_program->uniformLocation("preserveOpacity"); m_minBoundsUniform = m_program->uniformLocation("minBounds"); m_maxBoundsUniform = m_program->uniformLocation("maxBounds"); + m_sliceFrameWidthUniform = m_program->uniformLocation("sliceFrameWidth"); m_initialized = true; } @@ -135,6 +136,11 @@ void ShaderHelper::release() m_program->release(); } +void ShaderHelper::setUniformValue(GLuint uniform, const QVector2D &value) +{ + m_program->setUniformValue(uniform, value); +} + void ShaderHelper::setUniformValue(GLuint uniform, const QVector3D &value) { m_program->setUniformValue(uniform, value); @@ -340,6 +346,14 @@ GLuint ShaderHelper::minBounds() return m_minBoundsUniform; } +GLuint ShaderHelper::sliceFrameWidth() +{ + + if (!m_initialized) + qFatal("Shader not initialized"); + return m_sliceFrameWidthUniform; +} + GLuint ShaderHelper::posAtt() { if (!m_initialized) diff --git a/src/datavisualization/utils/shaderhelper_p.h b/src/datavisualization/utils/shaderhelper_p.h index 853fcc8f..da010cbd 100644 --- a/src/datavisualization/utils/shaderhelper_p.h +++ b/src/datavisualization/utils/shaderhelper_p.h @@ -52,6 +52,7 @@ class ShaderHelper bool testCompile(); void bind(); void release(); + void setUniformValue(GLuint uniform, const QVector2D &value); void setUniformValue(GLuint uniform, const QVector3D &value); void setUniformValue(GLuint uniform, const QVector4D &value); void setUniformValue(GLuint uniform, const QMatrix4x4 &value); @@ -84,6 +85,7 @@ class ShaderHelper GLuint preserveOpacity(); GLuint maxBounds(); GLuint minBounds(); + GLuint sliceFrameWidth(); GLuint posAtt(); GLuint uvAtt(); @@ -128,6 +130,7 @@ class ShaderHelper GLuint m_preserveOpacityUniform; GLuint m_minBoundsUniform; GLuint m_maxBoundsUniform; + GLuint m_sliceFrameWidthUniform; GLboolean m_initialized; }; diff --git a/tests/volumetrictest/volumetrictest.cpp b/tests/volumetrictest/volumetrictest.cpp index a277c8b0..dadb86be 100644 --- a/tests/volumetrictest/volumetrictest.cpp +++ b/tests/volumetrictest/volumetrictest.cpp @@ -360,9 +360,11 @@ void VolumetricModifier::createVolume() { m_volumeItem = new QCustom3DVolume; m_volumeItem->setTextureFormat(QImage::Format_ARGB32); -// m_volumeItem->setRotation(QQuaternion::fromAxisAndAngle(1.0f, 1.0f, 0.0f, 10.0f)); + m_volumeItem->setRotation(QQuaternion::fromAxisAndAngle(1.0f, 1.0f, 0.0f, 20.0f)); m_volumeItem->setPosition(QVector3D(xMiddle - (xRange / 2.0f), yMiddle + (yRange / 2.0f), zMiddle)); //m_volumeItem->setPosition(QVector3D(xMiddle, yMiddle, zMiddle)); + m_volumeItem->setDrawSliceFrames(true); + m_volumeItem->setDrawSlices(true); QImage logo; logo.load(QStringLiteral(":/logo_no_padding.png")); -- cgit v1.2.3 From 637fd9964133c4e60d9ffad32cf5307836397aad Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Fri, 12 Sep 2014 13:37:27 +0300 Subject: Documentation for volumetric example MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: Ibf613607b55e5ed0b56354d5da426870624d79ad Reviewed-by: Tomi Korpipää --- .../volumetric/doc/src/volumetric.qdoc | 104 ++++++++++++++++++++- .../datavisualization/volumetric/volumetric.cpp | 39 ++++++-- 2 files changed, 135 insertions(+), 8 deletions(-) diff --git a/examples/datavisualization/volumetric/doc/src/volumetric.qdoc b/examples/datavisualization/volumetric/doc/src/volumetric.qdoc index 48651cb1..39616670 100644 --- a/examples/datavisualization/volumetric/doc/src/volumetric.qdoc +++ b/examples/datavisualization/volumetric/doc/src/volumetric.qdoc @@ -21,7 +21,109 @@ \title Volumetric rendering Example \ingroup qtdatavisualization_examples \brief Rendering volumetric objects. + \since QtDataVisualization 1.2 + + This example shows how to use QCustom3DVolume items to display volumetric data. + + \image volumetric-example.png + + \section1 Initializing volume item + + The QCustom3DVolume items are special custom items (see QCustom3DItem), which can be used + to display volumetric data. The volume items are only supported with orthographic projection, + so first we make sure the graph is using it: + + \snippet volumetric/volumetric.cpp 6 + + The following code shows how to create a volumetric item tied to the data ranges of the axes: + + \snippet volumetric/volumetric.cpp 0 + + By setting the QCustom3DItem::scalingAbsolute property to \c{false}, we indicate that the + scaling of the volume should follow the changes in the data ranges. Next we define the + internal contents of the volume: + + \snippet volumetric/volumetric.cpp 1 + + We use eight bit indexed color for our texture, as it is compact and makes it easy to adjust the + colors without needing to reset the whole texture. For the texture data we use the data we + created earlier based on some height maps. + Typically the data for volume items comes pregenerated in a form of a stack of images, so we are + not going to explain the data generation in detail. Please refer to the example code if you + are interested in the actual data generation process. + + Since we are using eight bit indexed colors, we need a color table to map the eight bit color + indexes to actual colors. We use one we populated on our own, but in a typical use case you + would get the color table from the source images: + + \snippet volumetric/volumetric.cpp 2 + + We want to optionally show slice frames around the volume, so we initialize their properties. + Initially, the frames will be hidden: + + \snippet volumetric/volumetric.cpp 5 + + Finally we add the volume as a custom item to the graph to display it: + + \snippet volumetric/volumetric.cpp 3 + + \section1 Slicing into the volume + + Unless the volume is largely transparent, you can only see the surface of it, which is often + not very helpful. One way to inspect the internal structure of the volume is to view the slices + of the volume. QCustom3DVolume provides two ways to display the slices. The first is to show + the selected slices in place of the volume. For example, to specify a slice perpendicular to + the X-axis, you can use the following method: + + \snippet volumetric/volumetric.cpp 7 + + To actually draw the slice specified above, the QCustom3DVolume::drawSlices property must be + also set: + + \snippet volumetric/volumetric.cpp 8 + + The second way to view slices is to use QCustom3DVolume::renderSlice() method, which produces + a QImage from the specified slice. This image can then be displayed on another widget, such + as a QLabel here: + + \snippet volumetric/volumetric.cpp 9 + + \section1 Adjusting volume transparency + + Sometimes viewing just the slices doesn't give you a good understanding of the volume's internal + structure. QCustom3DVolume provides two properties that can be used to adjust the volume + transparency: + + \snippet volumetric/volumetric.cpp 11 + \dots + \snippet volumetric/volumetric.cpp 10 + + The QCustom3DVolume::alphaMultiplier is a general multiplier that is applied to the alpha value + of each voxel of the volume. It makes it possible to add uniform transparency to the already + somewhat transparent portions of the volume to reveal internal opaque details. This multiplier + doesn't affect colors that are fully opaque, unless the QCustom3DVolume::preserveOpacity + property is set to \c{false}. + + An alternative way to adjust the transparency of the volume is adjust the alpha values of the + voxels directly. For eight bit indexed textures, this is done simply by modifying and + resetting the color table: + + \snippet volumetric/volumetric.cpp 12 + + \section1 High definition vs. low definition shader + + By default the volume rendering uses the high definition shader. It accounts for each + voxel of the volume with correct weight when ray-tracing the volume contents, + providing an accurate representation of even the finer details of the volume. + However, this is computationally very expensive, so the frame rate suffers. + If rendering speed is more important than pixel-perfect + accuracy of the volume contents, you can take the much faster low definition shader into use + by setting \c{false} for QCustom3DVolume::useHighDefShader property. The low definition shader + achieves the speed by making compromises on the accuracy, so it doesn't guarantee each voxel + of the volume will be sampled. This can lead to flickering and/or other rendering artifacts + on the finer details of the volume. + + \snippet volumetric/volumetric.cpp 13 - TODO \section1 Example contents */ diff --git a/examples/datavisualization/volumetric/volumetric.cpp b/examples/datavisualization/volumetric/volumetric.cpp index 80df4bd5..6672d964 100644 --- a/examples/datavisualization/volumetric/volumetric.cpp +++ b/examples/datavisualization/volumetric/volumetric.cpp @@ -81,7 +81,9 @@ VolumetricModifier::VolumetricModifier(Q3DScatter *scatter) m_graph->activeTheme()->setType(Q3DTheme::ThemeQt); m_graph->setShadowQuality(QAbstract3DGraph::ShadowQualityNone); m_graph->scene()->activeCamera()->setCameraPreset(Q3DCamera::CameraPresetFront); + //! [6] m_graph->setOrthoProjection(true); + //! [6] m_graph->activeTheme()->setBackgroundEnabled(false); toggleAreaAll(true); @@ -100,6 +102,7 @@ VolumetricModifier::VolumetricModifier(Q3DScatter *scatter) createVolume(lowDetailSize, 0, lowDetailSize, m_lowDetailData); excavateMineShaft(lowDetailSize, 0, m_mineShaftArray.size(), m_lowDetailData); + //! [0] m_volumeItem = new QCustom3DVolume; // Adjust water level to zero with a minor tweak to y-coordinate position and scaling m_volumeItem->setScaling( @@ -112,11 +115,14 @@ VolumetricModifier::VolumetricModifier(Q3DScatter *scatter) (m_graph->axisY()->max() + m_graph->axisY()->min()) / 2.0f, (m_graph->axisZ()->max() + m_graph->axisZ()->min()) / 2.0f)); m_volumeItem->setScalingAbsolute(false); + //! [0] + //! [1] m_volumeItem->setTextureWidth(lowDetailSize); m_volumeItem->setTextureHeight(lowDetailSize / 2); m_volumeItem->setTextureDepth(lowDetailSize); m_volumeItem->setTextureFormat(QImage::Format_Indexed8); m_volumeItem->setTextureData(new QVector(*m_lowDetailData)); + //! [1] // Generate color tables. m_colorTable1.resize(colorTableSize); @@ -164,15 +170,21 @@ VolumetricModifier::VolumetricModifier(Q3DScatter *scatter) m_colorTable2[airColorIndex] = qRgba(0, 0, 0, 0); m_colorTable2[mineShaftColorIndex] = qRgba(255, 255, 0, 255); + //! [2] m_volumeItem->setColorTable(m_colorTable1); + //! [2] + //! [5] m_volumeItem->setSliceFrameGaps(QVector3D(0.01f, 0.02f, 0.01f)); m_volumeItem->setSliceFrameThicknesses(QVector3D(0.0025f, 0.005f, 0.0025f)); m_volumeItem->setSliceFrameWidths(QVector3D(0.0025f, 0.005f, 0.0025f)); m_volumeItem->setDrawSliceFrames(false); + //! [5] handleSlicingChanges(); + //! [3] m_graph->addCustomItem(m_volumeItem); + //! [3] m_timer.start(0); #else @@ -259,9 +271,13 @@ void VolumetricModifier::adjustSliceX(int value) m_sliceIndexX--; if (m_volumeItem) { if (m_volumeItem->sliceIndexX() != -1) + //! [7] m_volumeItem->setSliceIndexX(m_sliceIndexX); - m_sliceLabelX->setPixmap(QPixmap::fromImage( - m_volumeItem->renderSlice(Qt::XAxis, m_sliceIndexX))); + //! [7] + //! [9] + m_sliceLabelX->setPixmap( + QPixmap::fromImage(m_volumeItem->renderSlice(Qt::XAxis, m_sliceIndexX))); + //! [9] } } @@ -273,8 +289,8 @@ void VolumetricModifier::adjustSliceY(int value) if (m_volumeItem) { if (m_volumeItem->sliceIndexY() != -1) m_volumeItem->setSliceIndexY(m_sliceIndexY); - m_sliceLabelY->setPixmap(QPixmap::fromImage( - m_volumeItem->renderSlice(Qt::YAxis, m_sliceIndexY))); + m_sliceLabelY->setPixmap( + QPixmap::fromImage(m_volumeItem->renderSlice(Qt::YAxis, m_sliceIndexY))); } } @@ -286,8 +302,8 @@ void VolumetricModifier::adjustSliceZ(int value) if (m_volumeItem) { if (m_volumeItem->sliceIndexZ() != -1) m_volumeItem->setSliceIndexZ(m_sliceIndexZ); - m_sliceLabelZ->setPixmap(QPixmap::fromImage( - m_volumeItem->renderSlice(Qt::ZAxis, m_sliceIndexZ))); + m_sliceLabelZ->setPixmap( + QPixmap::fromImage(m_volumeItem->renderSlice(Qt::ZAxis, m_sliceIndexZ))); } } @@ -406,7 +422,9 @@ void VolumetricModifier::changeColorTable(int enabled) void VolumetricModifier::setPreserveOpacity(bool enabled) { + //! [10] m_volumeItem->setPreserveOpacity(enabled); + //! [10] // Rerender image labels adjustSliceX(m_sliceSliderX->value()); @@ -416,6 +434,7 @@ void VolumetricModifier::setPreserveOpacity(bool enabled) void VolumetricModifier::setTransparentGround(bool enabled) { + //! [12] int newAlpha = enabled ? terrainTransparency : 255; for (int i = aboveWaterGroundColorsMin; i < underWaterGroundColorsMax; i++) { QRgb oldColor1 = m_colorTable1.at(i); @@ -427,7 +446,7 @@ void VolumetricModifier::setTransparentGround(bool enabled) m_volumeItem->setColorTable(m_colorTable1); else m_volumeItem->setColorTable(m_colorTable2); - + //! [12] adjustSliceX(m_sliceSliderX->value()); adjustSliceY(m_sliceSliderY->value()); adjustSliceZ(m_sliceSliderZ->value()); @@ -435,7 +454,9 @@ void VolumetricModifier::setTransparentGround(bool enabled) void VolumetricModifier::setUseHighDefShader(bool enabled) { + //! [13] m_volumeItem->setUseHighDefShader(enabled); + //! [13] } void VolumetricModifier::adjustAlphaMultiplier(int value) @@ -445,7 +466,9 @@ void VolumetricModifier::adjustAlphaMultiplier(int value) mult = float(value - 99) / 2.0f; else mult = float(value) / float(500 - value * 4); + //! [11] m_volumeItem->setAlphaMultiplier(mult); + //! [11] QString labelFormat = QStringLiteral("Alpha multiplier: %1"); m_alphaMultiplierLabel->setText(labelFormat.arg( QString::number(m_volumeItem->alphaMultiplier(), 'f', 3))); @@ -628,7 +651,9 @@ void VolumetricModifier::handleSlicingChanges() if (m_volumeItem) { if (m_slicingX || m_slicingY || m_slicingZ) { // Only show slices of selected dimensions + //! [8] m_volumeItem->setDrawSlices(true); + //! [8] m_volumeItem->setSliceIndexX(m_slicingX ? m_sliceIndexX : -1); m_volumeItem->setSliceIndexY(m_slicingY ? m_sliceIndexY : -1); m_volumeItem->setSliceIndexZ(m_slicingZ ? m_sliceIndexZ : -1); -- cgit v1.2.3 From 9cc5a9f312dfb982044f5756bb5b857657378b02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomi=20Korpip=C3=A4=C3=A4?= Date: Mon, 15 Sep 2014 12:45:03 +0300 Subject: Fixed renderToImage for ES2 Task-number: QTRD-3305 Change-Id: I656cc878e6e83fe09d627b418f9cf624672a18b3 Reviewed-by: Miikka Heikkinen --- src/datavisualization/engine/qabstract3dgraph.cpp | 6 ++++++ src/datavisualization/utils/qutils.h | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/src/datavisualization/engine/qabstract3dgraph.cpp b/src/datavisualization/engine/qabstract3dgraph.cpp index 03eeedf6..a1cd7c56 100644 --- a/src/datavisualization/engine/qabstract3dgraph.cpp +++ b/src/datavisualization/engine/qabstract3dgraph.cpp @@ -545,6 +545,8 @@ QAbstract3DGraph::ElementType QAbstract3DGraph::selectedElement() const * \since QtDataVisualization 1.1 * * \return rendered image. + * + * \note OpenGL ES2 does not support anitialiasing, so \a msaaSamples is always forced to \c{0}. */ QImage QAbstract3DGraph::renderToImage(int msaaSamples, const QSize &imageSize) { @@ -975,8 +977,12 @@ QImage QAbstract3DGraphPrivate::renderToImage(int msaaSamples, const QSize &imag // Render the wanted frame offscreen m_context->makeCurrent(m_offscreenSurface); fboFormat.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil); +#ifdef QT_OPENGL_ES_2 + Q_UNUSED(msaaSamples); +#else fboFormat.setInternalTextureFormat(GL_RGB); fboFormat.setSamples(msaaSamples); +#endif fbo = new QOpenGLFramebufferObject(imageSize, fboFormat); if (fbo->isValid()) { QRect originalViewport = m_visualController->m_scene->viewport(); diff --git a/src/datavisualization/utils/qutils.h b/src/datavisualization/utils/qutils.h index b4ac17b4..92bd632c 100644 --- a/src/datavisualization/utils/qutils.h +++ b/src/datavisualization/utils/qutils.h @@ -36,10 +36,16 @@ inline static QSurfaceFormat qDefaultSurfaceFormat(bool antialias = true) // Antialias not supported for ES antialias = false; surfaceFormat.setRenderableType(QSurfaceFormat::OpenGLES); + surfaceFormat.setAlphaBufferSize(8); + surfaceFormat.setRedBufferSize(8); + surfaceFormat.setBlueBufferSize(8); + surfaceFormat.setGreenBufferSize(8); #endif if (antialias) surfaceFormat.setSamples(8); + else + surfaceFormat.setSamples(0); return surfaceFormat; } -- cgit v1.2.3 From b6d5edf9775f6bf2aa4f91906763bd1c492d47e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomi=20Korpip=C3=A4=C3=A4?= Date: Tue, 16 Sep 2014 07:31:30 +0300 Subject: Invalid slice usage crash fixed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Task-number: QTRD-3301 Change-Id: I0e39348a2e9392b4c1f0bd7ef26c5613272460e2 Reviewed-by: Pasi Keränen Reviewed-by: Titta Heikkala --- src/datavisualization/engine/bars3drenderer.cpp | 11 +++++++++++ src/datavisualization/engine/q3dscene.cpp | 9 +++++++-- src/datavisualization/engine/surface3drenderer.cpp | 5 +++++ 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/datavisualization/engine/bars3drenderer.cpp b/src/datavisualization/engine/bars3drenderer.cpp index 3e83a830..da774239 100644 --- a/src/datavisualization/engine/bars3drenderer.cpp +++ b/src/datavisualization/engine/bars3drenderer.cpp @@ -456,6 +456,11 @@ void Bars3DRenderer::render(GLuint defaultFboHandle) void Bars3DRenderer::drawSlicedScene() { + if (!m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionSlice)) { + qWarning("Selection mode QAbstract3DGraph::SelectionSlice not set. It must be set before calling setSlicingActive(true)."); + return; + } + GLfloat barPosX = 0; QVector3D lightPos; QVector4D lightColor = Utils::vectorFromColor(m_cachedTheme->lightColor()); @@ -815,6 +820,12 @@ void Bars3DRenderer::drawSlicedScene() int labelCount = m_sliceCache->labelItems().size(); for (int labelNo = 0; labelNo < labelCount; labelNo++) { + // Check for invalid usage (no selection when setting slicing active) + if (!firstVisualSliceArray) { + qWarning("No slice data found. Make sure there is a valid selection."); + continue; + } + // Get labels from first series only const BarRenderSliceItem &item = firstVisualSliceArray->at(labelNo); m_dummyBarRenderItem.setTranslation(QVector3D(item.translation().x(), diff --git a/src/datavisualization/engine/q3dscene.cpp b/src/datavisualization/engine/q3dscene.cpp index 3f2deec0..081868ef 100644 --- a/src/datavisualization/engine/q3dscene.cpp +++ b/src/datavisualization/engine/q3dscene.cpp @@ -98,7 +98,10 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION /*! * \qmlproperty bool Scene3D::slicingActive * - * This property contains whether 2D slicing view is currently active or not. + * This property contains whether 2D slicing view is currently active or not. If setting it, you + * must make sure AbstractGraph3D::selectionMode has + * \l{QAbstract3DGraph::SelectionSlice}{AbstractGraph3D.SelectionSlice} flag set in conjunction + * with either row or column selection mode, and there is a valid selection. * \note Not all visualizations support the 2D slicing view. */ @@ -291,7 +294,9 @@ QPoint Q3DScene::invalidSelectionPoint() /*! * \property Q3DScene::slicingActive * - * This property contains whether 2D slicing view is currently active or not. + * This property contains whether 2D slicing view is currently active or not. If setting it, you + * must make sure QAbstract3DGraph::selectionMode has QAbstract3DGraph::SelectionSlice flag set + * in conjunction with either row or column selection mode, and there is a valid selection. * \note Not all visualizations support the 2D slicing view. */ bool Q3DScene::isSlicingActive() const diff --git a/src/datavisualization/engine/surface3drenderer.cpp b/src/datavisualization/engine/surface3drenderer.cpp index fb7322cc..19ae0de4 100644 --- a/src/datavisualization/engine/surface3drenderer.cpp +++ b/src/datavisualization/engine/surface3drenderer.cpp @@ -782,6 +782,11 @@ void Surface3DRenderer::render(GLuint defaultFboHandle) void Surface3DRenderer::drawSlicedScene() { + if (!m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionSlice)) { + qWarning("Selection mode QAbstract3DGraph::SelectionSlice not set. It must be set before calling setSlicingActive(true)."); + return; + } + QVector3D lightPos; QVector4D lightColor = Utils::vectorFromColor(m_cachedTheme->lightColor()); -- cgit v1.2.3 From ce6aff50ecd1445d148c1bbb9f712d6bfb77a78e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomi=20Korpip=C3=A4=C3=A4?= Date: Tue, 16 Sep 2014 07:49:46 +0300 Subject: Prevent selection label generation on rotate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Task-number: QTRD-3234 Selection pointer label needs to be regenrated only when either theme or label text changes. Change-Id: Iabe91a88b719b186c998cda9e712207752762279 Reviewed-by: Pasi Keränen --- src/datavisualization/engine/selectionpointer.cpp | 10 ++++++---- src/datavisualization/engine/selectionpointer_p.h | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/datavisualization/engine/selectionpointer.cpp b/src/datavisualization/engine/selectionpointer.cpp index c40034ba..f7f8344b 100644 --- a/src/datavisualization/engine/selectionpointer.cpp +++ b/src/datavisualization/engine/selectionpointer.cpp @@ -211,10 +211,12 @@ void SelectionPointer::setRotation(const QQuaternion &rotation) m_rotation = rotation; } -void SelectionPointer::setLabel(const QString &label) +void SelectionPointer::setLabel(const QString &label, bool themeChange) { - m_label = label; - m_drawer->generateLabelItem(m_labelItem, m_label); + if (themeChange || m_label != label) { + m_label = label; + m_drawer->generateLabelItem(m_labelItem, m_label); + } } void SelectionPointer::setPointerObject(ObjectHelper *object) @@ -230,7 +232,7 @@ void SelectionPointer::setLabelObject(ObjectHelper *object) void SelectionPointer::handleDrawerChange() { m_cachedTheme = m_drawer->theme(); - setLabel(m_label); + setLabel(m_label, true); } void SelectionPointer::updateBoundingRect(const QRect &rect) diff --git a/src/datavisualization/engine/selectionpointer_p.h b/src/datavisualization/engine/selectionpointer_p.h index 7dc28024..08382b9f 100644 --- a/src/datavisualization/engine/selectionpointer_p.h +++ b/src/datavisualization/engine/selectionpointer_p.h @@ -49,7 +49,7 @@ public: void render(GLuint defaultFboHandle = 0, bool useOrtho = false); void setPosition(const QVector3D &position); - void setLabel(const QString &label); + void setLabel(const QString &label, bool themeChange = false); void setPointerObject(ObjectHelper *object); void setLabelObject(ObjectHelper *object); void handleDrawerChange(); -- cgit v1.2.3 From 7805902c5b056f94a8f8302eebf7da180bffe0b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomi=20Korpip=C3=A4=C3=A4?= Date: Tue, 16 Sep 2014 10:27:06 +0300 Subject: Fix to slice crash bug fix Task-number: QTRD-3301 Change-Id: Ia0ae6d5e3ac9aabc9af9199df27bbbe7f7908d60 Reviewed-by: Miikka Heikkinen --- src/datavisualization/engine/bars3drenderer.cpp | 7 +++++-- src/datavisualization/engine/q3dscene.cpp | 11 ++++++----- src/datavisualization/engine/surface3drenderer.cpp | 7 +++++-- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/src/datavisualization/engine/bars3drenderer.cpp b/src/datavisualization/engine/bars3drenderer.cpp index da774239..1f395422 100644 --- a/src/datavisualization/engine/bars3drenderer.cpp +++ b/src/datavisualization/engine/bars3drenderer.cpp @@ -456,8 +456,11 @@ void Bars3DRenderer::render(GLuint defaultFboHandle) void Bars3DRenderer::drawSlicedScene() { - if (!m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionSlice)) { - qWarning("Selection mode QAbstract3DGraph::SelectionSlice not set. It must be set before calling setSlicingActive(true)."); + if (m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionRow) + == m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionColumn)) { + qWarning("Invalid selection mode. Either QAbstract3DGraph::SelectionRow or" + " QAbstract3DGraph::SelectionColumn must be set before calling" + " setSlicingActive(true)."); return; } diff --git a/src/datavisualization/engine/q3dscene.cpp b/src/datavisualization/engine/q3dscene.cpp index 081868ef..08721b97 100644 --- a/src/datavisualization/engine/q3dscene.cpp +++ b/src/datavisualization/engine/q3dscene.cpp @@ -99,9 +99,10 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION * \qmlproperty bool Scene3D::slicingActive * * This property contains whether 2D slicing view is currently active or not. If setting it, you - * must make sure AbstractGraph3D::selectionMode has - * \l{QAbstract3DGraph::SelectionSlice}{AbstractGraph3D.SelectionSlice} flag set in conjunction - * with either row or column selection mode, and there is a valid selection. + * must make sure AbstractGraph3D::selectionMode has either + * \l{QAbstract3DGraph::SelectionRow}{AbstractGraph3D.SelectionRow} or + * \l{QAbstract3DGraph::SelectionColumn}{AbstractGraph3D.SelectionColumn} flag set, and there is a + * valid selection. * \note Not all visualizations support the 2D slicing view. */ @@ -295,8 +296,8 @@ QPoint Q3DScene::invalidSelectionPoint() * \property Q3DScene::slicingActive * * This property contains whether 2D slicing view is currently active or not. If setting it, you - * must make sure QAbstract3DGraph::selectionMode has QAbstract3DGraph::SelectionSlice flag set - * in conjunction with either row or column selection mode, and there is a valid selection. + * must make sure QAbstract3DGraph::selectionMode has either QAbstract3DGraph::SelectionRow or + * QAbstract3DGraph::SelectionColumn flag set, and there is a valid selection. * \note Not all visualizations support the 2D slicing view. */ bool Q3DScene::isSlicingActive() const diff --git a/src/datavisualization/engine/surface3drenderer.cpp b/src/datavisualization/engine/surface3drenderer.cpp index 19ae0de4..9bee6b30 100644 --- a/src/datavisualization/engine/surface3drenderer.cpp +++ b/src/datavisualization/engine/surface3drenderer.cpp @@ -782,8 +782,11 @@ void Surface3DRenderer::render(GLuint defaultFboHandle) void Surface3DRenderer::drawSlicedScene() { - if (!m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionSlice)) { - qWarning("Selection mode QAbstract3DGraph::SelectionSlice not set. It must be set before calling setSlicingActive(true)."); + if (m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionRow) + == m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionColumn)) { + qWarning("Invalid selection mode. Either QAbstract3DGraph::SelectionRow or" + " QAbstract3DGraph::SelectionColumn must be set before calling" + " setSlicingActive(true)."); return; } -- cgit v1.2.3 From abcb511f9ea94a7654a1f27c2215bbf8d9ce9607 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Wed, 17 Sep 2014 10:26:09 +0300 Subject: Fix volume frame translation for rotated volumes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Task-number: QTRD-3323 Change-Id: I08823f257a1a273da97a9e9e8597e4ffdb87d93b Reviewed-by: Tomi Korpipää --- src/datavisualization/engine/abstract3drenderer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/datavisualization/engine/abstract3drenderer.cpp b/src/datavisualization/engine/abstract3drenderer.cpp index 4dd58490..f0f332fe 100644 --- a/src/datavisualization/engine/abstract3drenderer.cpp +++ b/src/datavisualization/engine/abstract3drenderer.cpp @@ -1532,7 +1532,7 @@ void Abstract3DRenderer::drawVolumeSliceFrame(const CustomRenderItem *item, Qt:: float maxMult = (1.0f - item->maxBoundsNormal().x()) / range; fracTrans = fracTrans - ((1.0f - fracTrans) * minMult) + ((1.0f + fracTrans) * maxMult); if (needRotate) - translation -= rotationMatrix.map(QVector3D(fracTrans * item->scaling().x(), 0.0f, 0.0f)); + translation += rotationMatrix.map(QVector3D(fracTrans * item->scaling().x(), 0.0f, 0.0f)); else translation.setX(translation.x() + fracTrans * item->scaling().x()); frameScaling = QVector3D(item->scaling().z() -- cgit v1.2.3 From 3e795fd544331f4f38bcc667410f9c931f82bdf4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomi=20Korpip=C3=A4=C3=A4?= Date: Wed, 17 Sep 2014 11:25:06 +0300 Subject: Fixed custom item wrong side reflection Task-number: QTRD-3311 Change-Id: Ia5aea90259c249a5ffd48e16fae4e2dc33902afd Reviewed-by: Miikka Heikkinen --- ...tdatavisualization-qml-abstractdeclarative.qdoc | 4 + .../engine/abstract3drenderer.cpp | 6 +- src/datavisualization/engine/qabstract3dgraph.cpp | 4 + tests/barstest/barstest.pro | 2 + tests/barstest/barstest.qrc | 6 + tests/barstest/chart.cpp | 88 +- tests/barstest/chart.h | 1 + tests/barstest/main.cpp | 8 +- tests/barstest/shuttle.obj | 6349 ++++++++++++++++++++ tests/barstest/shuttle.png | Bin 0 -> 1361 bytes 10 files changed, 6436 insertions(+), 32 deletions(-) create mode 100644 tests/barstest/barstest.qrc create mode 100644 tests/barstest/shuttle.obj create mode 100644 tests/barstest/shuttle.png diff --git a/src/datavisualization/doc/src/qtdatavisualization-qml-abstractdeclarative.qdoc b/src/datavisualization/doc/src/qtdatavisualization-qml-abstractdeclarative.qdoc index 5dc7b35c..b1d74dd6 100644 --- a/src/datavisualization/doc/src/qtdatavisualization-qml-abstractdeclarative.qdoc +++ b/src/datavisualization/doc/src/qtdatavisualization-qml-abstractdeclarative.qdoc @@ -357,6 +357,10 @@ * * \note Affects only Bars3D. * + * \note In Bars3D graphs holding both positive and negative values, reflections are not supported + * for custom items that intersect the floor plane. In that case, reflections should be turned off + * to avoid incorrect rendering. + * * \sa reflectivity */ diff --git a/src/datavisualization/engine/abstract3drenderer.cpp b/src/datavisualization/engine/abstract3drenderer.cpp index f0f332fe..a0e0bc45 100644 --- a/src/datavisualization/engine/abstract3drenderer.cpp +++ b/src/datavisualization/engine/abstract3drenderer.cpp @@ -1291,8 +1291,11 @@ void Abstract3DRenderer::drawCustomItems(RenderingState state, while (loopCount < 2) { foreach (CustomRenderItem *item, m_customRenderCache) { // Check that the render item is visible, and skip drawing if not - if (!item->isVisible()) + // Also check if reflected item is on the "wrong" side, and skip drawing if it is + if (!item->isVisible() || ((m_reflectionEnabled && reflection < 0.0f) + && (m_yFlipped == item->translation().y() >= 0.0))) { continue; + } if (loopCount == 0) { if (item->isVolume()) { volumeDetected = true; @@ -1434,7 +1437,6 @@ void Abstract3DRenderer::drawCustomItems(RenderingState state, shader->setUniformValue(shader->minBounds(), item->minBounds()); shader->setUniformValue(shader->maxBounds(), item->maxBounds()); - QVector3D slices; if (shader == m_volumeTextureSliceShader) { shader->setUniformValue(shader->volumeSliceIndices(), item->sliceFractions()); diff --git a/src/datavisualization/engine/qabstract3dgraph.cpp b/src/datavisualization/engine/qabstract3dgraph.cpp index a1cd7c56..d906666c 100644 --- a/src/datavisualization/engine/qabstract3dgraph.cpp +++ b/src/datavisualization/engine/qabstract3dgraph.cpp @@ -729,6 +729,10 @@ qreal QAbstract3DGraph::horizontalAspectRatio() const * * \note Affects only Q3DBars. * + * \note In Q3DBars graphs holding both positive and negative values, reflections are not supported + * for custom items that intersect the floor plane. In that case, reflections should be turned off + * to avoid incorrect rendering. + * * \sa reflectivity */ void QAbstract3DGraph::setReflection(bool enable) diff --git a/tests/barstest/barstest.pro b/tests/barstest/barstest.pro index f213ea9e..56e24ef2 100644 --- a/tests/barstest/barstest.pro +++ b/tests/barstest/barstest.pro @@ -5,4 +5,6 @@ SOURCES += main.cpp chart.cpp custominputhandler.cpp HEADERS += chart.h custominputhandler.h +RESOURCES += barstest.qrc + QT += widgets diff --git a/tests/barstest/barstest.qrc b/tests/barstest/barstest.qrc new file mode 100644 index 00000000..f7237eb7 --- /dev/null +++ b/tests/barstest/barstest.qrc @@ -0,0 +1,6 @@ + + + shuttle.obj + shuttle.png + + diff --git a/tests/barstest/chart.cpp b/tests/barstest/chart.cpp index f451e480..137ce88e 100644 --- a/tests/barstest/chart.cpp +++ b/tests/barstest/chart.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -241,7 +242,6 @@ GraphModifier::GraphModifier(Q3DBars *barchart, QColorDialog *colorDialog) QObject::connect(m_graph, &QAbstract3DGraph::currentFpsChanged, this, &GraphModifier::handleFpsChange); - resetTemperatureData(); } @@ -626,33 +626,33 @@ void GraphModifier::changeTheme() m_graph->setActiveTheme(m_builtinTheme); switch (theme) { - case Q3DTheme::ThemeQt: - qDebug() << __FUNCTION__ << "ThemeQt"; - break; - case Q3DTheme::ThemePrimaryColors: - qDebug() << __FUNCTION__ << "ThemePrimaryColors"; - break; - case Q3DTheme::ThemeDigia: - qDebug() << __FUNCTION__ << "ThemeDigia"; - break; - case Q3DTheme::ThemeStoneMoss: - qDebug() << __FUNCTION__ << "ThemeStoneMoss"; - break; - case Q3DTheme::ThemeArmyBlue: - qDebug() << __FUNCTION__ << "ThemeArmyBlue"; - break; - case Q3DTheme::ThemeRetro: - qDebug() << __FUNCTION__ << "ThemeRetro"; - break; - case Q3DTheme::ThemeEbony: - qDebug() << __FUNCTION__ << "ThemeEbony"; - break; - case Q3DTheme::ThemeIsabelle: - qDebug() << __FUNCTION__ << "ThemeIsabelle"; - break; - default: - qDebug() << __FUNCTION__ << "Unknown theme"; - break; + case Q3DTheme::ThemeQt: + qDebug() << __FUNCTION__ << "ThemeQt"; + break; + case Q3DTheme::ThemePrimaryColors: + qDebug() << __FUNCTION__ << "ThemePrimaryColors"; + break; + case Q3DTheme::ThemeDigia: + qDebug() << __FUNCTION__ << "ThemeDigia"; + break; + case Q3DTheme::ThemeStoneMoss: + qDebug() << __FUNCTION__ << "ThemeStoneMoss"; + break; + case Q3DTheme::ThemeArmyBlue: + qDebug() << __FUNCTION__ << "ThemeArmyBlue"; + break; + case Q3DTheme::ThemeRetro: + qDebug() << __FUNCTION__ << "ThemeRetro"; + break; + case Q3DTheme::ThemeEbony: + qDebug() << __FUNCTION__ << "ThemeEbony"; + break; + case Q3DTheme::ThemeIsabelle: + qDebug() << __FUNCTION__ << "ThemeIsabelle"; + break; + default: + qDebug() << __FUNCTION__ << "Unknown theme"; + break; } if (++theme > Q3DTheme::ThemeIsabelle) @@ -1128,7 +1128,7 @@ void GraphModifier::changeValueAxisFormat(const QString & text) void GraphModifier::changeLogBase(const QString &text) { QLogValue3DAxisFormatter *formatter = - qobject_cast(m_graph->valueAxis()->formatter()); + qobject_cast(m_graph->valueAxis()->formatter()); if (formatter) formatter->setBase(qreal(text.toDouble())); } @@ -1723,3 +1723,33 @@ void GraphModifier::setReflectivity(int value) qreal reflectivity = (qreal)value / 100.0; m_graph->setReflectivity(reflectivity); } + +void GraphModifier::toggleCustomItem() +{ + static int counter = 0; + int state = ++counter % 3; + + QVector3D positionOne = QVector3D(6.0f, -15.0f, 3.0f); + QVector3D positionTwo = QVector3D(2.0f, 18.0f, 3.0f); + + if (state == 0) { + m_graph->removeCustomItemAt(positionTwo); + } else if (state == 1) { + QCustom3DItem *item = new QCustom3DItem(); + item->setMeshFile(":/shuttle.obj"); + item->setPosition(positionOne); + item->setScaling(QVector3D(0.1f, 0.1f, 0.1f)); + item->setRotation(QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, rand())); + item->setTextureImage(QImage(":/shuttle.png")); + m_graph->addCustomItem(item); + } else { + m_graph->removeCustomItemAt(positionOne); + QCustom3DItem *item = new QCustom3DItem(); + item->setMeshFile(":/shuttle.obj"); + item->setPosition(positionTwo); + item->setScaling(QVector3D(0.1f, 0.1f, 0.1f)); + item->setRotation(QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, rand())); + item->setTextureImage(QImage(":/shuttle.png")); + m_graph->addCustomItem(item); + } +} diff --git a/tests/barstest/chart.h b/tests/barstest/chart.h index 91be6e04..a5061df4 100644 --- a/tests/barstest/chart.h +++ b/tests/barstest/chart.h @@ -99,6 +99,7 @@ public: void setInputHandlerSelectionEnabled(int enabled); void setReflection(bool enabled); void setReflectivity(int value); + void toggleCustomItem(); public slots: void flipViews(); diff --git a/tests/barstest/main.cpp b/tests/barstest/main.cpp index 0dc29eb1..409ecbab 100644 --- a/tests/barstest/main.cpp +++ b/tests/barstest/main.cpp @@ -262,6 +262,9 @@ int main(int argc, char **argv) reflectivitySlider->setValue(50); reflectivitySlider->setMaximum(100); + QPushButton *toggleCustomItemButton = new QPushButton(widget); + toggleCustomItemButton->setText(QStringLiteral("Toggle Custom Item")); + QSlider *spacingSliderX = new QSlider(Qt::Horizontal, widget); spacingSliderX->setTickInterval(1); spacingSliderX->setMinimum(0); @@ -431,7 +434,8 @@ int main(int argc, char **argv) vLayout3->addWidget(cameraTargetSliderY, 0, Qt::AlignTop); vLayout3->addWidget(cameraTargetSliderZ, 0, Qt::AlignTop); vLayout3->addWidget(reflectionCheckBox, 0, Qt::AlignTop); - vLayout3->addWidget(reflectivitySlider, 1, Qt::AlignTop); + vLayout3->addWidget(reflectivitySlider, 0, Qt::AlignTop); + vLayout3->addWidget(toggleCustomItemButton, 1, Qt::AlignTop); widget->show(); @@ -564,6 +568,8 @@ int main(int argc, char **argv) &GraphModifier::setReflection); QObject::connect(reflectivitySlider, &QSlider::valueChanged, modifier, &GraphModifier::setReflectivity); + QObject::connect(toggleCustomItemButton, &QPushButton::clicked, modifier, + &GraphModifier::toggleCustomItem); QObject::connect(staticCheckBox, &QCheckBox::stateChanged, addDataButton, &QPushButton::setEnabled); diff --git a/tests/barstest/shuttle.obj b/tests/barstest/shuttle.obj new file mode 100644 index 00000000..e872228d --- /dev/null +++ b/tests/barstest/shuttle.obj @@ -0,0 +1,6349 @@ +# Blender v2.66 (sub 0) OBJ File: '' +# www.blender.org +v -0.290924 -0.416888 -3.733797 +v -0.033253 -0.399965 -3.916942 +v 0.098892 -0.335648 -4.014968 +v 0.110144 -0.419894 -4.016005 +v -0.014627 -0.351984 -3.919619 +v -0.003375 -0.436229 -3.920655 +v 0.077510 -0.338845 -3.987319 +v 0.088762 -0.423090 -3.988356 +v 0.268419 -0.311615 -4.127978 +v 0.279672 -0.395861 -4.129015 +v -0.216945 -0.357152 -3.636585 +v -0.442480 -0.421494 -3.440589 +v 0.040557 -0.350366 -3.819523 +v -0.366883 -0.356351 -3.352865 +v -0.059636 -0.414591 -3.440213 +v 0.195164 -0.388461 -3.624492 +v -0.508658 -0.419445 -3.066802 +v -0.205255 -0.426666 -3.173799 +v -0.446409 -0.327120 -2.985436 +v -0.301084 -0.444031 -2.817294 +v 0.151449 -0.262081 -3.165398 +v 0.405315 -0.264481 -3.348743 +v 0.818570 -0.244023 -3.649532 +v 0.829822 -0.328268 -3.650568 +v 0.438945 -0.298561 -3.337861 +v 0.450197 -0.382806 -3.338897 +v 0.010751 -0.257630 -2.926187 +v 0.476024 -0.294278 -3.283418 +v 0.487276 -0.378524 -3.284455 +v 0.893196 -0.234640 -3.602006 +v 0.904448 -0.318886 -3.603042 +v -0.575146 -0.405155 -2.358083 +v -0.526846 -0.291701 -2.296914 +v -0.123567 -0.205514 -2.586394 +v -0.401501 -0.443667 -2.166967 +v 0.438519 -0.375468 -2.805563 +v 0.688118 -0.342410 -2.991890 +v 0.304996 -0.393955 -2.597466 +v -0.651696 -0.393273 -1.762192 +v -0.615966 -0.265853 -1.716287 +v -0.263638 -0.153966 -1.992210 +v 0.143186 -0.430595 -2.279088 +v -0.512839 -0.438185 -1.613670 +v -0.710028 -0.388098 -1.448538 +v -0.679965 -0.261349 -1.410043 +v -0.813314 -0.388657 -1.206769 +v 0.751287 -0.156495 -2.401505 +v 0.998622 -0.165467 -2.585768 +v -0.031714 -0.447371 -1.755604 +v -0.583894 -0.434779 -1.325088 +v -0.408965 -0.117462 -1.482493 +v -0.790573 -0.268054 -1.169135 +v 0.625851 -0.150158 -2.234187 +v -1.051885 -0.397310 -0.885646 +v -1.031232 -0.284379 -0.851625 +v -0.707410 -0.437077 -1.088477 +v -1.334166 -0.408930 -0.570468 +v 0.404917 -0.087146 -1.938298 +v -0.498689 -0.116097 -1.212783 +v -1.314745 -0.307881 -0.538558 +v -0.959224 -0.446984 -0.774490 +v 1.798825 0.967755 -3.015114 +v 2.046406 0.986555 -3.209870 +v 1.795686 1.006585 -3.015357 +v 2.043267 1.025386 -3.210112 +v 1.806945 0.929656 -3.008470 +v 2.054526 0.948456 -3.203225 +v 1.797665 1.044451 -3.009186 +v 2.045245 1.063251 -3.203941 +v -0.218636 -0.452499 -1.294639 +v -0.644958 -0.132219 -0.977335 +v 1.804675 1.079696 -2.996872 +v 2.052256 1.098496 -3.191627 +v -1.248682 -0.457011 -0.466167 +v 1.840314 0.837931 -2.974904 +v 2.087895 0.856731 -3.169660 +v 1.646588 -0.144702 -2.733368 +v 1.657840 -0.228948 -2.734405 +v 1.737599 -0.131659 -2.805524 +v 1.748852 -0.215905 -2.806561 +v 1.819258 1.148577 -2.971684 +v 1.033279 0.836881 -2.319959 +v 1.031777 0.970096 -2.298396 +v 1.060381 0.758531 -2.296534 +v -0.899523 -0.158764 -0.676483 +v 0.945538 0.814187 -2.200939 +v 0.170080 -0.031614 -1.496071 +v 1.043881 1.032762 -2.270612 +v 0.966443 0.743790 -2.180778 +v 0.952423 0.936921 -2.181507 +v 1.858652 1.239977 -2.912781 +v -0.317238 -0.452926 -1.060344 +v -1.192492 -0.196285 -0.374663 +v 1.856681 0.583228 -2.822182 +v 2.018811 -0.094129 -2.886386 +v 0.967854 0.995533 -2.156932 +v 1.272977 -0.200327 -2.268122 +v 1.284228 -0.284573 -2.269158 +v 1.076506 1.117039 -2.211863 +v -0.484562 -0.458987 -0.834904 +v 1.848687 0.788896 -2.757349 +v 2.078178 -0.164712 -2.848704 +v 1.967059 0.229003 -2.794663 +v 1.967832 0.643840 -2.831535 +v 2.215413 0.662641 -3.026290 +v 0.936685 0.816251 -2.042840 +v 2.039912 0.250063 -2.839813 +v 1.003257 1.074974 -2.104949 +v 1.164321 0.598239 -2.181823 +v 0.950019 0.768391 -2.028775 +v 0.943810 0.900014 -2.031005 +v 2.521226 0.390336 -3.213021 +v 1.936351 1.129229 -2.824698 +v 2.183932 1.148030 -3.019454 +v 2.568967 0.404915 -3.250601 +v 1.055366 0.603082 -2.081103 +v 0.869869 0.465058 -1.923758 +v 0.936491 -0.173930 -1.917023 +v 2.705161 0.067033 -3.318110 +v 2.096944 0.264363 -2.850901 +v 1.957182 0.386704 -2.747152 +v 2.029313 0.077472 -2.774992 +v 0.955724 0.940103 -2.015164 +v 1.928438 1.372002 -2.811322 +v -0.765050 -0.471609 -0.536370 +v 2.030367 0.402476 -2.793896 +v 2.100080 0.103611 -2.820803 +v 2.514476 0.522913 -3.173254 +v 2.574949 0.263657 -3.196595 +v 2.562289 0.536088 -3.211256 +v 2.622122 0.279579 -3.234349 +v 2.598488 0.409527 -3.214962 +v -0.064938 0.006053 -1.100592 +v 2.088244 0.403277 -2.809050 +v 2.151782 0.130884 -2.833575 +v 2.199769 1.159790 -2.964559 +v 2.193588 1.233527 -2.965299 +v 2.221596 1.161935 -2.979915 +v 2.215481 1.234891 -2.980646 +v 2.136538 0.271664 -2.829406 +v 1.137830 1.220917 -2.137315 +v 0.872108 0.654248 -1.875077 +v 0.961190 -0.247342 -1.861737 +v 2.739614 0.051854 -3.275776 +v -2.850674 0.163164 1.082772 +v -2.774223 -0.444295 1.078949 +v -2.508742 -0.433973 0.871016 +v -2.566129 0.205717 0.857016 +v 0.982043 0.994775 -1.981001 +v -1.069794 -0.481435 -0.242789 +v 2.592936 0.518564 -3.182256 +v 2.642673 0.305340 -3.201453 +v -2.926099 0.145051 1.155134 +v -2.869277 -0.451273 1.165592 +v 1.009718 0.673075 -1.961110 +v 2.235091 1.170649 -2.961918 +v 1.045386 1.162465 -2.033586 +v 2.230007 1.231294 -2.962527 +v 2.325998 0.322675 -2.951759 +v 1.972627 1.185781 -2.753756 +v 2.129131 0.389939 -2.793774 +v 2.183229 0.158016 -2.814654 +v 2.227766 1.099078 -2.934829 +v 1.117407 -0.306283 -1.939579 +v 1.981589 1.144577 -2.746340 +v 1.973910 1.228192 -2.748031 +v 2.249297 1.101867 -2.950499 +v 2.210880 1.300532 -2.936848 +v 1.886303 0.951739 -2.651532 +v 2.165588 -0.207364 -2.762840 +v 1.360371 -0.269775 -2.128519 +v 2.232590 1.301186 -2.952497 +v 2.160973 0.274542 -2.798066 +v 0.937019 0.410104 -1.852872 +v 0.960798 0.265444 -1.858128 +v 2.041237 1.191461 -2.784518 +v 2.048705 1.157124 -2.778339 +v 2.042305 1.226803 -2.779748 +v 2.258118 1.120717 -2.937466 +v 2.320933 0.422137 -2.921925 +v 2.366302 0.227636 -2.939436 +v 1.976683 1.431691 -2.744228 +v 2.244230 1.286403 -2.939127 +v -3.086442 -0.474834 1.379359 +v 1.999431 1.110853 -2.726914 +v 1.985242 1.265353 -2.730038 +v -3.023720 -0.458462 1.334241 +v -3.024019 0.109883 1.282283 +v -2.844489 0.344693 1.122684 +v -2.737176 -0.511595 1.117650 +v -2.469550 -0.506414 0.908517 +v -2.551327 0.395732 0.889431 +v 1.000046 -0.329019 -1.811242 +v 2.154800 0.373104 -2.768372 +v 2.199881 0.179835 -2.785773 +v -2.914064 0.322285 1.189905 +v -2.834171 -0.518740 1.204847 +v 1.179604 1.267508 -2.087104 +v 2.051748 1.257770 -2.764754 +v 2.063573 1.129020 -2.762150 +v 2.080589 1.195258 -2.775463 +v -0.196284 0.002337 -0.889996 +v 1.013546 1.060481 -1.927888 +v -3.072142 -0.445996 1.396327 +v 2.603875 0.637948 -3.126857 +v 2.707508 0.193661 -3.166857 +v 2.085482 1.172761 -2.771414 +v 2.081289 1.218414 -2.772337 +v 1.075555 1.201415 -1.986324 +v 2.002330 0.508317 -2.645192 +v 2.127263 -0.027287 -2.693413 +v 2.556508 0.625865 -3.087951 +v 2.661251 0.176820 -3.128379 +v 2.074001 0.520013 -2.695353 +v 2.194747 0.002364 -2.741958 +v 1.000951 0.830916 -1.880131 +v 2.095224 1.154349 -2.760807 +v 2.087476 1.238703 -2.762513 +v 2.023438 1.089743 -2.698434 +v 2.004898 1.291606 -2.702516 +v -3.122983 -0.461461 1.459483 +v -3.158215 -0.041663 1.448402 +v -2.497997 0.004146 0.929450 +v -2.509563 0.149734 0.925099 +v 1.003036 0.858653 -1.877204 +v 2.270079 1.067661 -2.884072 +v 1.005703 0.814744 -1.874867 +v 2.291162 1.070783 -2.900280 +v 2.128013 0.510403 -2.719236 +v 2.238064 0.038604 -2.761713 +v 2.068128 1.279648 -2.741818 +v 2.083579 1.111428 -2.738417 +v 2.247011 1.342852 -2.886831 +v 2.268338 1.343057 -2.903010 +v -2.993610 -0.524459 1.373357 +v -2.994892 0.277606 1.300703 +v 2.627505 0.603237 -3.112099 +v 2.713651 0.233920 -3.145348 +v 2.292918 1.094878 -2.895721 +v 1.006938 0.871768 -1.872413 +v 2.105035 1.197866 -2.757530 +v 2.093582 1.187722 -2.747088 +v 2.092232 1.203828 -2.747250 +v 2.273946 1.321208 -2.897990 +v 0.912103 0.803812 -1.788658 +v 1.019079 -0.292614 -1.771378 +v 2.804159 0.046327 -3.194257 +v 2.107537 1.186366 -2.755460 +v 2.105394 1.209702 -2.755932 +v 2.245164 0.292374 -2.778011 +v 0.747334 -0.381459 -1.545246 +v 2.108331 1.142823 -2.745258 +v 2.098208 1.253036 -2.747486 +v 2.099697 1.174463 -2.740594 +v 2.096009 1.218463 -2.741035 +v 2.112516 1.176954 -2.750038 +v 2.108556 1.220074 -2.750911 +v 1.015678 0.889476 -1.861661 +v 2.162992 0.481149 -2.717304 +v 2.256692 0.079446 -2.753470 +v 1.035607 1.090416 -1.893256 +v 2.119216 1.171063 -2.742090 +v 2.114042 1.227400 -2.743229 +v -3.036469 -0.488219 1.432582 +v 1.026486 0.781917 -1.851175 +v 2.241119 0.356951 -2.758555 +v 2.270656 0.230325 -2.769956 +v -0.400720 -0.022989 -0.663568 +v 2.108939 1.167601 -2.729508 +v 2.352467 0.499374 -2.857929 +v 2.431048 0.162488 -2.888259 +v 2.103901 1.227706 -2.730110 +v 2.078094 0.869479 -2.669583 +v 2.325675 0.888279 -2.864338 +v 2.049954 1.084459 -2.665236 +v 2.029886 1.302955 -2.669655 +v 2.105674 1.107026 -2.710752 +v 2.088951 1.289105 -2.714434 +v 2.107442 1.198835 -2.720560 +v 2.122808 1.139938 -2.727132 +v 2.111851 1.259233 -2.729544 +v 2.126617 1.169588 -2.732825 +v 2.121016 1.230568 -2.734058 +v 2.126010 1.200245 -2.735167 +v 2.126854 1.200309 -2.735831 +v -3.255541 -0.463337 1.614298 +v -3.256979 -0.068785 1.579187 +v -3.090151 -0.525402 1.492390 +v -3.140838 0.071873 1.477060 +v 2.183017 0.449113 -2.704647 +v 2.261101 0.114361 -2.734786 +v 1.025626 0.910940 -1.843900 +v 2.118830 1.168976 -2.716800 +v 2.113792 1.229081 -2.717403 +v 2.133590 1.172756 -2.723654 +v 2.128416 1.229093 -2.724793 +v -2.405642 -0.551479 0.981773 +v -2.505313 0.545503 0.958747 +v 1.032741 0.920593 -1.832253 +v 2.139077 1.180082 -2.715972 +v 2.135116 1.223202 -2.716844 +v 2.126722 1.178219 -2.705876 +v 2.123034 1.222219 -2.706317 +v 2.136451 1.146135 -2.709190 +v 2.126328 1.256348 -2.711419 +v 2.142239 1.190453 -2.710951 +v 2.140096 1.213790 -2.711423 +v 2.330166 1.100055 -2.847870 +v 2.130499 1.192853 -2.699662 +v 2.129149 1.208959 -2.699824 +v 2.142597 1.202290 -2.709353 +v 2.311194 1.326385 -2.850139 +v -0.678415 -0.058034 -0.389918 +v 2.126498 1.116483 -2.683367 +v 2.111047 1.284703 -2.686769 +v 2.335972 1.077011 -2.842714 +v 2.315368 1.073955 -2.825890 +v 2.297923 0.301154 -2.738834 +v 2.313149 1.349285 -2.845444 +v -3.224716 -0.523658 1.644234 +v -3.228977 0.037748 1.596002 +v 2.259607 0.406750 -2.716804 +v 2.310765 0.187427 -2.736550 +v 2.292301 1.349146 -2.828649 +v 1.051190 0.836263 -1.810548 +v 2.074942 1.095809 -2.632374 +v 2.056402 1.297672 -2.636457 +v 1.051411 0.836718 -1.810222 +v 1.051764 0.833314 -1.810103 +v 2.147183 1.160469 -2.694164 +v 2.139435 1.244822 -2.695870 +v 1.051937 0.833724 -1.809843 +v 1.051832 0.835286 -1.809826 +v 1.052011 0.834070 -1.809716 +v 1.052027 0.833906 -1.809712 +v 1.051999 0.834825 -1.809658 +v 1.051961 0.835391 -1.809651 +v 1.052122 0.834297 -1.809553 +v 2.289008 0.296555 -2.724424 +v 1.946327 1.084103 -2.529010 +v 2.264997 -0.237532 -2.656099 +v -3.146048 -0.468059 1.586396 +v 2.295856 0.334163 -2.728890 +v 2.310954 0.269436 -2.734717 +v 2.287534 0.325511 -2.715739 +v 2.300741 0.268886 -2.720837 +v 2.153370 1.180757 -2.684340 +v 2.149177 1.226410 -2.685263 +v -2.995660 -0.411552 1.480706 +v 1.055648 0.923143 -1.800017 +v 2.154070 1.203913 -2.681214 +v -0.987628 -0.106173 -0.106669 +v -3.134648 -0.466717 1.601017 +v 2.142878 1.138361 -2.660432 +v 2.131053 1.267111 -2.663036 +v 2.305306 0.359619 -2.707547 +v 2.331457 0.247507 -2.717641 +v 2.296714 0.347997 -2.697108 +v 2.319591 0.249920 -2.705938 +v 2.094598 1.122062 -2.604852 +v 2.080408 1.276562 -2.607976 +v 0.470274 -0.415629 -1.195124 +v 2.682583 0.683203 -3.020018 +v 2.802250 0.170184 -3.066206 +v 2.692932 0.640855 -3.023288 +v 2.792405 0.214405 -3.061682 +v 2.636060 0.671604 -2.979968 +v 2.757006 0.153092 -3.026651 +v 2.359883 1.134861 -2.806733 +v 2.152321 1.169328 -2.645438 +v 2.145921 1.239007 -2.646847 +v 2.345995 1.300547 -2.808394 +v 2.153390 1.204670 -2.640668 +v 2.412149 0.533689 -2.776916 +v 2.502886 0.144687 -2.811939 +v 0.970224 0.925229 -1.686331 +v 1.091354 -0.325301 -1.665935 +v 2.881699 0.045075 -3.095805 +v 2.371721 1.118882 -2.793227 +v 2.351501 1.116275 -2.775872 +v -3.112347 -0.464090 1.629621 +v 2.105930 1.159222 -2.586859 +v 2.098250 1.242837 -2.588550 +v 2.355013 1.318201 -2.795225 +v 2.334614 1.317729 -2.777893 +v 2.314089 0.357987 -2.673523 +v 2.340505 0.244738 -2.683719 +v 1.104303 1.101391 -1.798523 +v 2.090404 0.561258 -2.516102 +v 2.159124 0.571179 -2.570591 +v 2.234664 -0.057203 -2.571783 +v 2.205596 0.557038 -2.605525 +v 2.229047 0.520855 -2.620486 +v 2.238063 0.482200 -2.623966 +v 2.298549 -0.026551 -2.624406 +v 2.332671 0.012252 -2.654572 +v 2.295672 0.428428 -2.663943 +v 2.328226 0.095662 -2.658767 +v 2.337243 0.057009 -2.662247 +v 2.319328 0.300003 -2.670594 +v 2.323741 0.370700 -2.680527 +v 2.354745 0.175176 -2.686744 +v 2.343450 0.307384 -2.690079 +v 2.353938 0.241245 -2.692182 +v 1.085896 0.905799 -1.761274 +v 2.107213 1.201633 -2.581135 +v 1.172914 1.208536 -1.853034 +v -3.524472 -0.463065 1.962955 +v -3.542270 -0.167984 1.949735 +v 2.374104 1.189970 -2.783334 +v 2.369021 1.250615 -2.783942 +v 1.303689 1.278820 -1.940955 +v -2.749923 0.604098 1.287890 +v -2.601352 -0.583194 1.281085 +v -2.331104 -0.584296 1.070484 +v -2.444929 0.666788 1.044342 +v 2.335002 0.352805 -2.651304 +v 2.357880 0.254727 -2.660134 +v -2.812832 0.575628 1.346300 +v -2.702155 -0.590712 1.367115 +v 2.388829 1.185178 -2.765078 +v 2.382714 1.258133 -2.765809 +v 2.346222 0.364438 -2.655067 +v 2.372374 0.252327 -2.665161 +v 2.368793 1.183281 -2.747422 +v 2.362612 1.257019 -2.748161 +v -3.502416 -0.514638 1.987533 +v -3.528153 -0.092777 1.968860 +v -2.930290 -0.491180 1.547024 +v 2.066685 0.851305 -2.470213 +v -3.080119 -0.460294 1.670958 +v 2.353852 0.333838 -2.636405 +v 2.367060 0.277213 -2.641503 +v 2.140192 0.869385 -2.515599 +v -2.873451 -0.595312 1.528083 +v -2.875654 0.517249 1.427634 +v 2.123721 1.449962 -2.555544 +v 2.366726 0.342510 -2.637991 +v 2.381824 0.277783 -2.643818 +v 2.365586 0.306170 -2.632818 +v 0.180044 -0.433739 -0.860263 +v 2.390810 0.196854 -2.633883 +v 2.339653 0.416178 -2.614137 +v 2.379757 0.310792 -2.633873 +v -2.972414 -0.594417 1.632153 +v -3.043199 0.236465 1.611041 +v 2.197619 0.881876 -2.526829 +v 1.155619 0.140929 -1.644002 +v 2.061041 1.010198 -2.426111 +v 2.124709 0.698580 -2.447134 +v 2.629037 1.066507 -2.869623 +v 2.771686 0.621339 -2.939622 +v 2.857832 0.252023 -2.972872 +v 2.676305 1.087418 -2.904726 +v 2.483987 0.515887 -2.700598 +v 2.562568 0.179001 -2.730927 +v 2.134736 1.022953 -2.472975 +v 2.196270 0.721780 -2.493294 +v -3.115630 -0.588979 1.772415 +v -3.122642 0.192029 1.706162 +v 2.305190 0.463503 -2.547947 +v 2.383274 0.128751 -2.578085 +v 2.026136 1.180204 -2.395136 +v 2.372061 -0.253901 -2.533144 +v 0.043832 -0.439397 -0.700086 +v -2.331525 -0.123197 1.132486 +v 2.610039 1.166097 -2.837585 +v 2.673477 0.969915 -2.869029 +v 2.777325 0.659725 -2.919367 +v 2.880958 0.215439 -2.959367 +v 2.657509 1.185952 -2.873027 +v 2.720273 0.991850 -2.904138 +v 2.379762 0.373280 -2.580731 +v 2.409298 0.246654 -2.592131 +v 2.731814 0.647876 -2.878239 +v 2.836558 0.198831 -2.918667 +v -3.039373 -0.455495 1.723220 +v 2.192646 1.021841 -2.487980 +v 2.248729 0.747345 -2.506499 +v 2.309599 0.498417 -2.529263 +v 2.403300 0.096714 -2.565429 +v 2.237351 0.888547 -2.505383 +v -0.183302 -0.450597 -0.489896 +v 2.405254 0.311230 -2.572676 +v 1.043932 1.013192 -1.572568 +v 1.174857 -0.343976 -1.550015 +v 2.968846 0.048153 -2.984724 +v 2.704206 1.090705 -2.868940 +v 1.193313 1.053879 -1.685613 +v 2.300203 0.530684 -2.498384 +v 2.410253 0.058885 -2.540860 +v -2.870028 -0.379944 1.617982 +v -0.502937 -0.466927 -0.212460 +v 2.262925 0.542265 -2.453038 +v 2.383671 0.024616 -2.499642 +v 2.688581 1.172611 -2.842590 +v 2.740755 1.011263 -2.868450 +v 2.233117 1.007717 -2.472307 +v 2.280868 0.774003 -2.488075 +v -2.681424 0.688036 1.389168 +v -2.372814 0.754289 1.142473 +v -2.249194 -0.603433 1.170773 +v 2.433319 0.963376 -2.621027 +v -2.742923 0.657642 1.444079 +v 2.197806 0.531341 -2.394472 +v 2.322739 -0.004263 -2.442693 +v -3.420844 -0.571247 2.087621 +v -3.456852 0.016609 2.061712 +v 2.261785 0.891425 -2.474043 +v -0.828435 -0.477984 0.061043 +v 2.548733 0.450740 -2.649420 +v 2.594102 0.256238 -2.666930 +v 2.619376 1.253520 -2.777794 +v 2.736592 0.891025 -2.835893 +v 2.666746 1.272449 -2.813868 +v 2.782720 0.913795 -2.871353 +v -2.802757 0.594778 1.513184 +v 2.842664 0.549920 -2.883518 +v 2.892401 0.336695 -2.902714 +v 2.419066 1.038090 -2.596991 +v 2.466659 0.890910 -2.620581 +v 2.366409 0.398028 -2.496960 +v 2.411491 0.204759 -2.514360 +v -3.768498 -0.285858 2.351028 +v -3.754395 -0.436849 2.353898 +v -2.979417 0.290761 1.695520 +v 2.258257 0.990733 -2.446479 +v 2.298049 0.795972 -2.459619 +v -3.744207 -0.468465 2.368351 +v -3.763825 -0.250991 2.363676 +v -2.991889 -0.449902 1.784124 +v 2.589037 0.355701 -2.637096 +v -3.058914 0.242857 1.779885 +v 2.405318 0.303321 -2.484666 +v 2.886850 0.445733 -2.870009 +v 2.109286 1.132686 -2.326646 +v 2.219562 0.592947 -2.363060 +v 2.696261 1.244513 -2.793414 +v 2.792664 0.946380 -2.841198 +v 1.301631 1.132067 -1.690738 +v 2.383062 0.419848 -2.468079 +v 2.437160 0.187925 -2.488959 +v 2.181364 1.141334 -2.376844 +v 2.287943 0.619687 -2.412037 +v 2.862712 0.573808 -2.851874 +v 2.922544 0.317299 -2.874968 +v 2.818116 0.561038 -2.810024 +v 2.878590 0.301782 -2.833364 +v 2.426071 1.103677 -2.552133 +v 2.514010 0.831724 -2.595722 +v 2.235144 1.129736 -2.400364 +v 2.332283 0.654296 -2.432440 +v 2.346390 0.907367 -2.454136 +v -3.411176 0.052851 2.120724 +v 2.429752 0.306199 -2.453326 +v 2.269301 1.099582 -2.397708 +v 2.352008 0.694778 -2.425019 +v 2.386485 0.438404 -2.426523 +v 2.450023 0.166012 -2.451047 +v 2.915866 0.448472 -2.835623 +v 2.344078 0.972432 -2.436077 +v 2.370150 0.844828 -2.444686 +v 2.871840 0.434359 -2.793598 +v -2.786263 -0.483209 1.702896 +v 1.459973 1.193063 -1.759240 +v -3.703011 -0.503725 2.422886 +v -3.730082 -0.199896 2.416091 +v 2.655625 1.315469 -2.699349 +v 2.808775 0.841848 -2.775261 +v 2.702611 1.333742 -2.736255 +v 2.854139 0.865139 -2.811362 +v 2.288410 1.067288 -2.384314 +v 2.357333 0.729951 -2.407073 +v 2.357592 0.441017 -2.374193 +v 2.427305 0.142152 -2.401100 +v 1.807099 1.188967 -2.008747 +v -3.783568 -0.334437 2.490638 +v -3.776477 -0.407414 2.491810 +v 2.122243 1.235844 -2.255761 +v 2.482100 -0.255753 -2.399353 +v 2.726073 1.295463 -2.728897 +v 2.852031 0.905933 -2.791331 +v 2.295756 0.426582 -2.312892 +v 2.367887 0.117350 -2.340733 +v -2.939741 -0.443760 1.851008 +v -3.781106 -0.318610 2.496646 +v -3.771103 -0.424172 2.498541 +v 2.441322 0.304925 -2.409196 +v 2.453266 1.150154 -2.493283 +v 2.568163 0.794829 -2.550234 +v 1.130007 1.063856 -1.452342 +v 1.265938 -0.347821 -1.428686 +v 3.061789 0.055427 -2.865868 +v -2.602579 0.736106 1.497157 +v -2.292120 0.804178 1.248855 +v -2.434953 -0.604050 1.489534 +v -2.163494 -0.608053 1.278256 +v -2.664054 0.704644 1.549387 +v -2.539160 -0.611934 1.572914 +v 2.399283 0.915534 -2.415008 +v -3.707198 -0.182800 2.448233 +v 2.363834 1.022590 -2.395347 +v 2.408991 0.801573 -2.410258 +v -2.722515 -0.616791 1.721958 +v -2.725142 0.639161 1.608672 +v -2.829179 -0.615824 1.810674 +v -2.909245 0.322942 1.786891 +v 2.392228 0.914706 -2.400276 +v 2.398101 0.948794 -2.405777 +v 2.411428 0.883567 -2.410178 +v -2.983486 -0.609514 1.936485 +v -2.991769 0.272910 1.861910 +v 2.388079 0.936458 -2.393279 +v 2.401934 0.893610 -2.400146 +v 1.266405 1.148829 -1.537915 +v 1.276485 1.330618 -1.562468 +v 1.301959 1.399716 -1.588675 +v 1.829089 1.191557 -1.980541 +v 2.385908 3.043540 -2.584738 +v 2.432531 3.084452 -2.624845 +v 2.484531 3.138930 -2.670390 +v 2.555058 3.180480 -2.729192 +v 2.587869 1.699016 -2.618729 +v 2.897153 3.208163 -2.998451 +v 2.950947 3.055532 -3.026375 +v 2.946595 3.189529 -3.035288 +v 2.959600 3.134547 -3.040378 +v 2.417759 0.294566 -2.355182 +v -3.764574 -0.295328 2.521762 +v -3.750702 -0.443027 2.524510 +v 2.358011 0.275051 -2.293223 +v -3.320640 -0.590025 2.214050 +v -3.361390 0.074490 2.184798 +v 2.390117 0.955552 -2.380219 +v 2.415719 0.876379 -2.392909 +v 2.408200 0.974432 -2.384957 +v 2.431282 0.861455 -2.392579 +v -3.753472 -0.287501 2.537197 +v 2.398036 0.969083 -2.363085 +v 2.431485 0.865638 -2.379665 +v -3.651074 -0.516133 2.490283 +v -3.681587 -0.172421 2.482510 +v 2.311849 1.347830 -2.326247 +v -3.741101 -0.282712 2.553676 +v -3.725441 -0.449869 2.556817 +v 2.175858 1.240742 -2.187508 +v 1.176857 1.068079 -1.392566 +v 1.312589 -0.342230 -1.368881 +v -2.560177 0.739860 1.551322 +v -2.121265 -0.602981 1.332435 +v -2.249778 0.807875 1.303072 +v -2.622366 0.708349 1.602724 +v -2.686267 0.642614 1.658664 +v -2.872984 0.326449 1.833357 +v -2.958486 0.276123 1.904730 +v -3.335886 0.076990 2.217503 +v -3.668144 -0.171074 2.499706 +v -3.734634 -0.282056 2.561952 +v -3.734566 -0.369723 2.570150 +v -2.150712 -0.249451 1.324240 +v 1.267079 0.010084 -1.364303 +v 1.704978 -0.191159 -1.687236 +v 2.198494 1.185947 -2.198470 +v 2.267581 1.192809 -2.252964 +v 2.325830 0.562710 -2.240516 +v 2.313725 1.176653 -2.287457 +v 2.336207 1.139528 -2.301576 +v 2.434129 0.098705 -2.282343 +v 2.390649 0.590464 -2.293602 +v 2.344165 1.100576 -2.304204 +v 2.425891 0.627661 -2.324495 +v 2.423750 0.711053 -2.330483 +v 2.431708 0.672101 -2.333111 +v 2.400364 1.044399 -2.342861 +v 2.410625 0.974989 -2.344487 +v 2.420496 0.915260 -2.346699 +v 2.426872 0.985581 -2.358127 +v 2.452506 0.789191 -2.360079 +v 2.446831 0.863022 -2.362433 +v 2.444856 0.921557 -2.366269 +v 2.453526 0.855127 -2.366929 +v 2.496511 1.170443 -2.429398 +v 2.620876 0.785842 -2.491042 +v 2.713268 1.342514 -2.614195 +v 2.759644 1.360499 -2.652004 +v 2.773482 1.317705 -2.658864 +v 2.879037 0.829869 -2.696361 +v 2.909818 0.896081 -2.726441 +v 2.923656 0.853286 -2.733300 +v 2.434469 0.098745 -2.281906 +v -2.120000 -0.602832 1.334057 +v 1.313986 -0.342065 -1.367090 +v 2.435135 0.098824 -2.281052 +v 2.435757 0.098897 -2.280254 +v 2.436307 0.098962 -2.279548 +v 2.436763 0.099016 -2.278964 +v 2.436999 0.099043 -2.278662 +v -2.885212 -0.437337 1.920949 +v -2.717870 -0.356682 1.785125 +v -3.728000 -0.281169 2.570479 +v -3.712341 -0.448326 2.573620 +v -0.735546 -0.053125 0.219066 +v -0.406216 0.000394 -0.041418 +v -0.100273 0.040739 -0.281944 +v 0.172417 0.073341 -0.495491 +v 2.335518 1.350617 -2.295889 +v 0.354290 0.083209 -0.634777 +v -3.623852 -0.512926 2.525198 +v -3.654365 -0.169215 2.517424 +v 2.425971 0.972373 -2.327255 +v 2.459421 0.868929 -2.343834 +v 0.696977 0.054076 -0.892441 +v 2.948120 3.214165 -2.933081 +v 2.606057 3.186486 -2.663780 +v 2.997989 3.195582 -2.969370 +v 2.536222 3.145018 -2.604090 +v 3.012064 3.140727 -2.973088 +v 2.485188 3.090654 -2.557307 +v 1.046458 0.007453 -1.152257 +v -3.714744 -0.282939 2.586870 +v 2.439279 3.049827 -2.516283 +v 3.004908 3.061887 -2.957164 +v 2.449116 0.979252 -2.332477 +v 2.472199 0.866275 -2.340099 +v 1.368491 -0.052711 -1.393705 +v 1.717894 -0.070445 -1.662515 +v 1.478573 -0.058668 -1.476762 +v 2.441737 0.961632 -2.314012 +v 2.467338 0.882459 -2.326701 +v -3.309670 0.080582 2.251135 +v -3.268919 -0.583933 2.280387 +v 2.410137 0.281190 -2.226363 +v -3.701911 -0.287948 2.602134 +v -3.688039 -0.435647 2.604882 +v 2.482991 0.302250 -2.271514 +v 2.455522 0.944402 -2.306774 +v 2.469378 0.901553 -2.313642 +v -2.915858 -0.601549 2.023225 +v -2.924141 0.280876 1.948650 +v 2.468971 0.957141 -2.314878 +v 2.482299 0.891914 -2.319279 +v 2.465229 0.923305 -2.306645 +v -2.755682 -0.607167 1.904943 +v -2.835748 0.331599 1.881160 +v -2.646201 0.648459 1.709923 +v -2.643573 -0.607493 1.823210 +v 2.443879 1.032018 -2.292681 +v 2.489036 0.811001 -2.307592 +v -3.626724 -0.173321 2.551450 +v 2.481117 0.925173 -2.310048 +v -2.579668 0.714584 1.657621 +v -2.454774 -0.601995 1.681149 +v -2.516832 0.746206 1.607137 +v -2.349206 -0.593950 1.599513 +v -2.206373 0.814278 1.358835 +v -2.077747 -0.597953 1.388236 +v 2.667233 1.708364 -2.516935 +v 1.224686 1.075007 -1.330905 +v 1.360617 -0.336670 -1.307250 +v 3.156468 0.066579 -2.744432 +v 2.549224 1.161456 -2.370206 +v 2.664121 0.806131 -2.427157 +v 2.540023 0.316550 -2.282602 +v 1.385180 1.409518 -1.481935 +v -3.679715 -0.306668 2.626691 +v -3.669712 -0.412230 2.628586 +v 1.360989 1.340571 -1.454082 +v 2.400261 0.438891 -2.178853 +v 2.472391 0.129659 -2.206693 +v 2.831269 1.307854 -2.593973 +v 2.957226 0.918323 -2.656406 +v 2.230447 1.248588 -2.116977 +v 2.590304 -0.243009 -2.260568 +v 1.354370 1.159190 -1.425091 +v 1.917054 1.201918 -1.867717 +v -3.673127 -0.321429 2.632291 +v -3.666035 -0.394406 2.633464 +v -2.830682 -0.430915 1.990889 +v 2.473447 0.454663 -2.225597 +v 2.543159 0.155798 -2.252504 +v 2.410583 1.081677 -2.227614 +v 2.479506 0.744341 -2.250373 +v 3.001074 0.449623 -2.628313 +v 2.829160 1.348647 -2.573941 +v 2.980689 0.880044 -2.649048 +v 2.783530 1.330534 -2.535297 +v 2.936681 0.856913 -2.611208 +v 3.051332 0.464469 -2.662340 +v -3.572802 -0.488389 2.589894 +v -3.599872 -0.184558 2.583099 +v 1.591093 1.208507 -1.591064 +v 2.482720 0.988763 -2.258252 +v 2.508792 0.861158 -2.266861 +v 2.531323 0.455464 -2.240752 +v 2.594861 0.183071 -2.265276 +v 2.415908 1.116850 -2.209669 +v 2.498616 0.712046 -2.236978 +v 2.579618 0.323851 -2.261107 +v -2.645182 -0.466535 1.885712 +v 1.939045 1.204508 -1.839511 +v -3.258276 0.070860 2.316835 +v 2.506480 0.926224 -2.248802 +v 2.992383 0.581606 -2.586979 +v 3.052857 0.322350 -2.610320 +v 2.407334 1.150018 -2.179512 +v 2.504473 0.674577 -2.211588 +v 3.042733 0.595054 -2.621444 +v 3.102565 0.338545 -2.644537 +v 2.603378 1.124561 -2.324717 +v 2.691316 0.852608 -2.368305 +v 3.078396 0.468329 -2.624718 +v 2.370287 1.163586 -2.134528 +v 2.476867 0.641940 -2.169721 +v 2.572210 0.442126 -2.225475 +v 2.626308 0.210203 -2.246356 +v 1.491168 1.154391 -1.447635 +v 2.890636 1.267406 -2.544106 +v 2.987040 0.969274 -2.591890 +v 2.788228 0.379194 -2.381967 +v 2.304762 1.155709 -2.075926 +v 2.415038 0.615971 -2.112340 +v 2.604052 0.326729 -2.229767 +v -2.858987 0.266405 2.036315 +v -3.533523 -0.443650 2.638577 +v -3.553141 -0.226176 2.633901 +v 2.469866 1.015657 -2.175067 +v 2.509659 0.820896 -2.188207 +v -2.762138 0.316353 1.974206 +v -2.778535 -0.424773 2.057773 +v 3.071249 0.576878 -2.590723 +v 3.120985 0.363654 -2.609920 +v 2.781708 0.478212 -2.350957 +v 2.827077 0.283710 -2.368468 +v -3.539008 -0.258828 2.645375 +v -3.524905 -0.409819 2.648245 +v 2.597879 0.425291 -2.200073 +v 2.642961 0.232022 -2.217474 +v 2.650729 1.065375 -2.299858 +v 2.698322 0.918196 -2.323448 +v -2.569382 0.622266 1.812512 +v 2.900579 1.299991 -2.513951 +v 3.016554 0.941337 -2.571435 +v 2.855713 1.281357 -2.474664 +v 2.972929 0.918862 -2.532765 +v 2.506130 0.920204 -2.160643 +v -3.173446 -0.542107 2.404937 +v -3.209453 0.045748 2.379028 +v 2.445409 0.560504 -2.076893 +v 2.570342 0.024900 -2.125114 +v -2.493455 0.687025 1.764050 +v 2.684069 0.992910 -2.299412 +v -2.427931 0.717893 1.714301 +v -2.119321 0.784146 1.467606 +v -1.995701 -0.573576 1.495906 +v 2.487048 1.037626 -2.146613 +v 2.534799 0.803912 -2.162379 +v 2.942544 1.202524 -2.516853 +v 2.994718 1.041175 -2.542714 +v 2.517081 0.572200 -2.127054 +v 2.637826 0.054551 -2.173658 +v 2.571093 0.562590 -2.150938 +v 2.681143 0.090791 -2.193414 +v 1.466306 1.086034 -1.335468 +v 2.979094 1.123082 -2.516365 +v -0.568604 -0.447372 0.394570 +v 1.323830 1.046159 -1.213567 +v 1.454755 -0.311009 -1.191014 +v 3.248744 0.081120 -2.625723 +v 2.688243 0.344561 -2.209711 +v -2.593169 -0.347277 1.974947 +v 3.029114 0.682935 -2.497393 +v 3.133857 0.233890 -2.537821 +v 2.530565 0.923082 -2.129303 +v -0.224107 -0.434065 0.145862 +v 3.079074 0.695309 -2.532807 +v 3.182707 0.251022 -2.572807 +v 2.606071 0.533336 -2.149005 +v 2.699770 0.131633 -2.185170 +v 2.490887 1.056969 -2.105453 +v 2.546971 0.782472 -2.123972 +v 2.684199 0.409138 -2.190257 +v 2.713735 0.282512 -2.201657 +v 2.963027 1.221936 -2.481166 +v 3.025790 1.027834 -2.512276 +v 2.918829 1.202466 -2.441528 +v 2.982266 1.006286 -2.472972 +v -2.731051 -0.419180 2.118678 +v 2.346020 1.217882 -1.984849 +v 2.691945 -0.216224 -2.122858 +v 2.626097 0.501300 -2.136348 +v 2.704180 0.166548 -2.166486 +v 2.809263 0.554231 -2.283747 +v 2.887846 0.217345 -2.314077 +v -2.799153 0.230131 2.121073 +v -2.792140 -0.550877 2.187326 +v 0.122037 -0.414552 -0.095656 +v 3.101457 0.660216 -2.517043 +v 3.187603 0.290900 -2.550293 +v 2.461960 1.061494 -2.053272 +v 2.523494 0.760321 -2.073591 +v 3.006995 1.126368 -2.480578 +v 2.963268 1.105875 -2.440934 +v 2.399616 1.050076 -1.991851 +v 2.463283 0.738458 -2.012874 +v 2.541997 0.922437 -2.085123 +v -2.620849 -0.553009 2.083075 +v -2.691633 0.277873 2.061963 +v 0.380138 -0.399580 -0.262083 +v 2.741003 0.353341 -2.170536 +v 2.702686 0.458937 -2.148505 +v 2.753845 0.239614 -2.168251 +v 2.735783 0.349782 -2.158101 +v 2.738935 0.386350 -2.160591 +v 2.754033 0.321623 -2.166418 +v 2.501059 1.494406 -2.071566 +v -2.498046 0.561724 1.911958 +v -2.495843 -0.550836 2.012409 +v 2.518039 0.913888 -2.030968 +v 2.733885 0.378609 -2.149074 +v 2.747093 0.321983 -2.154171 +v 2.457638 0.897352 -1.968773 +v -2.690304 -0.414381 2.170940 +v -3.127855 -0.045628 2.482288 +v -3.102117 -0.467490 2.500962 +v 2.770798 1.227046 -2.232150 +v 2.748385 0.411806 -2.139249 +v 2.774536 0.299695 -2.149342 +v 2.764617 1.300784 -2.232889 +v 2.792626 1.229192 -2.247504 +v -2.409183 0.623172 1.864025 +v -2.298506 -0.543169 1.884841 +v 2.786510 1.302148 -2.248235 +v 2.741907 0.400740 -2.129507 +v 2.764784 0.302663 -2.138337 +v -2.529794 -0.443952 2.062569 +v -2.339763 0.652407 1.813967 +v -2.191192 -0.534884 1.807161 +v -1.920944 -0.535987 1.596561 +v -2.034769 0.715099 1.570418 +v -1.970078 -0.080625 1.596081 +v 1.720073 1.327862 -1.406896 +v 0.572047 -0.387414 -0.352526 +v 2.806120 1.237906 -2.229508 +v 2.801037 1.298551 -2.230116 +v -3.088440 -0.411708 2.522214 +v -3.106239 -0.116627 2.508994 +v 1.609069 1.259907 -1.293617 +v 2.543657 1.253039 -2.021345 +v 1.525799 0.957611 -1.197050 +v 2.757700 0.410246 -2.104644 +v 2.784117 0.296996 -2.114841 +v 2.533483 0.613445 -1.947803 +v 2.602202 0.623366 -2.002292 +v 2.677744 -0.005017 -2.003484 +v 2.648675 0.609224 -2.037226 +v 2.672127 0.573042 -2.052187 +v 2.681143 0.534388 -2.055668 +v 2.741627 0.025636 -2.056106 +v 2.775750 0.064439 -2.086273 +v 2.738751 0.480616 -2.095645 +v 2.771306 0.147849 -2.090468 +v 2.780322 0.109196 -2.093948 +v 2.762407 0.352190 -2.102295 +v 2.766821 0.422887 -2.112228 +v 2.797824 0.227363 -2.118445 +v 2.786530 0.359571 -2.121780 +v 2.797017 0.293432 -2.123883 +v 1.548968 1.153765 -1.228190 +v 2.798795 1.166335 -2.202419 +v 2.552618 1.211834 -2.013930 +v 2.544939 1.295449 -2.015621 +v 2.863513 0.586883 -2.198347 +v 2.954250 0.197881 -2.233369 +v 2.820327 1.169125 -2.218088 +v 2.781909 1.367789 -2.204438 +v 2.803619 1.368443 -2.220086 +v 1.423109 0.978571 -1.105456 +v 1.544240 -0.271959 -1.085059 +v 3.334585 0.098417 -2.514929 +v 1.554716 0.187936 -1.132116 +v 2.612265 1.258718 -2.052108 +v 3.101424 0.726458 -2.383560 +v 3.222370 0.207946 -2.430242 +v -2.658075 -0.410585 2.212276 +v 3.150617 0.738370 -2.420182 +v 3.270283 0.225352 -2.466369 +v 3.160928 0.696012 -2.423422 +v 3.260400 0.269561 -2.461816 +v 2.619734 1.224381 -2.045928 +v 2.613334 1.294060 -2.047338 +v 2.829147 1.187974 -2.205056 +v 2.815259 1.353660 -2.206717 +v 2.570461 1.178110 -1.994503 +v 2.556271 1.332609 -1.997628 +v 2.777033 0.404579 -2.081148 +v 2.799910 0.306502 -2.089978 +v 2.789302 0.416625 -2.086768 +v 2.815452 0.304514 -2.096862 +v 2.634603 1.196277 -2.029740 +v 2.622778 1.325027 -2.032343 +v 2.651619 1.262515 -2.043053 +v 1.554142 0.981857 -1.160642 +v 2.656512 1.240018 -2.039004 +v 2.652319 1.285671 -2.039927 +v -2.635774 -0.407958 2.240880 +v 2.794724 0.385259 -2.065313 +v 2.807933 0.328634 -2.070411 +v 2.809805 0.394697 -2.069692 +v 2.824903 0.329970 -2.075520 +v 2.806035 0.357461 -2.061383 +v -2.489068 -0.351828 2.132329 +v 2.463909 1.145066 -1.865152 +v 2.782581 -0.176570 -1.992240 +v 1.570340 0.895334 -1.144881 +v 1.570297 0.896443 -1.144827 +v 1.570363 0.895879 -1.144798 +v 1.570504 0.894974 -1.144707 +v 1.570489 0.895138 -1.144709 +v 1.570444 0.896371 -1.144649 +v 1.570624 0.894816 -1.144569 +v 2.666254 1.221606 -2.028398 +v 2.658506 1.305960 -2.030103 +v 1.570869 0.894455 -1.144293 +v 1.570594 0.897869 -1.144312 +v 2.594467 1.157000 -1.966023 +v 2.575927 1.358863 -1.970106 +v 1.570894 0.897475 -1.143969 +v 2.833890 0.249041 -2.065584 +v 2.782731 0.468365 -2.045837 +v -2.701300 -0.462008 2.315575 +v -2.705561 0.099398 2.267342 +v 2.841108 1.134918 -2.151662 +v -0.479044 -0.046272 0.545593 +v 2.862191 1.138039 -2.167869 +v 2.822836 0.362979 -2.065575 +v 2.654608 1.178685 -2.006007 +v 2.639158 1.346905 -2.009409 +v -2.624374 -0.406615 2.255502 +v 2.818040 1.410109 -2.154421 +v 2.839368 1.410315 -2.170599 +v 2.863947 1.162135 -2.163311 +v 2.676065 1.265123 -2.025119 +v 2.664612 1.254980 -2.014677 +v 2.663261 1.271085 -2.014839 +v 0.971816 -0.356390 -0.546463 +v 2.844975 1.388465 -2.165580 +v 2.678566 1.253623 -2.023050 +v 2.676423 1.276959 -2.023522 +v 2.679361 1.210080 -2.012848 +v 2.669238 1.320294 -2.015076 +v 2.670727 1.241720 -2.008183 +v 2.667038 1.285720 -2.008625 +v 2.683546 1.244211 -2.017628 +v 2.679586 1.287331 -2.018500 +v 1.579791 0.985026 -1.130600 +v -1.956412 0.610154 1.662774 +v -1.856740 -0.486828 1.685802 +v 2.690246 1.238319 -2.009680 +v 2.685071 1.294657 -2.010819 +v 2.679968 1.234858 -1.997098 +v 2.674930 1.294963 -1.997700 +v -0.132362 0.006287 0.310615 +v 1.590120 0.977428 -1.119874 +v 2.748270 0.515690 -1.979648 +v 2.826352 0.180938 -2.009786 +v -2.571993 0.138873 2.206668 +v -2.521306 -0.458402 2.221998 +v -2.685403 -0.396185 2.345565 +v -2.686840 -0.001633 2.310453 +v 2.620983 1.151716 -1.932826 +v 2.600915 1.370211 -1.937245 +v 2.676704 1.174282 -1.978342 +v 2.659981 1.356362 -1.982024 +v 2.678470 1.266092 -1.988150 +v 2.693838 1.207195 -1.994723 +v 2.682881 1.326489 -1.997135 +v 2.697646 1.236845 -2.000415 +v 2.692045 1.297825 -2.001648 +v 2.697040 1.267502 -2.002758 +v 2.651196 0.936979 -1.934514 +v 2.898778 0.955779 -2.129270 +v 2.929918 0.567420 -2.117638 +v 3.008498 0.230534 -2.147968 +v 2.689859 1.236233 -1.984390 +v 2.684821 1.296338 -1.984993 +v 2.822841 0.425467 -2.012432 +v 2.852378 0.298841 -2.023833 +v 1.608781 0.850501 -1.104316 +v 2.704620 1.240012 -1.991243 +v 2.699446 1.296350 -1.992383 +v 1.624309 1.159754 -1.138181 +v 2.752678 0.550604 -1.960964 +v 2.846378 0.148901 -1.997130 +v 1.606690 0.959087 -1.103622 +v 2.710106 1.247339 -1.983562 +v 2.706145 1.290459 -1.984434 +v 2.697752 1.245476 -1.973465 +v 2.694063 1.289476 -1.973907 +v 2.707481 1.213392 -1.976781 +v 2.697358 1.323606 -1.979010 +v -2.445962 -0.418610 2.191836 +v 3.233725 0.674674 -2.334944 +v 3.319870 0.305357 -2.368193 +v 2.848333 0.363417 -2.004377 +v 2.713269 1.257710 -1.978540 +v 2.711125 1.281046 -1.979012 +v 1.518181 0.875199 -1.011295 +v 1.625158 -0.221229 -0.994015 +v 3.410238 0.117713 -2.416893 +v 2.901195 1.167312 -2.115460 +v 2.701529 1.260111 -1.967251 +v 2.700178 1.276216 -1.967413 +v 2.713626 1.269547 -1.976943 +v 1.616466 0.943560 -1.090626 +v 2.882224 1.393643 -2.117728 +v -2.383910 0.349570 2.084356 +v -2.382627 -0.452496 2.157011 +v 2.697528 1.183740 -1.950957 +v 2.682078 1.351960 -1.954359 +v 2.907001 1.144267 -2.110304 +v 2.886398 1.141212 -2.093480 +v 2.743282 0.582871 -1.930085 +v 2.853333 0.111072 -1.972562 +v 1.623579 0.887518 -1.082372 +v 0.198902 0.047648 0.105888 +v 1.621282 0.931471 -1.084234 +v 2.884178 1.416542 -2.113034 +v 2.863331 1.416404 -2.096239 +v -2.503361 -0.388481 2.254219 +v -2.538594 0.031317 2.243138 +v -1.878374 0.077127 1.724185 +v -1.889940 0.222714 1.719833 +v 2.645971 1.163066 -1.899965 +v 2.627431 1.364929 -1.904047 +v 2.718213 1.227726 -1.961754 +v 2.710466 1.312080 -1.963460 +v 1.626047 0.904541 -1.078374 +v 3.189935 0.700514 -2.275981 +v 3.294679 0.251469 -2.316409 +v 3.238191 0.712702 -2.313742 +v 3.341825 0.268415 -2.353742 +v 2.706004 0.594452 -1.884739 +v 2.826750 0.076803 -1.931343 +v 2.640884 0.583528 -1.826173 +v 2.765818 0.047924 -1.874394 +v 1.714162 1.276631 -1.167239 +v 2.724400 1.248014 -1.951930 +v 2.720207 1.293667 -1.952853 +v 1.654904 1.136021 -1.105274 +v 2.725100 1.271170 -1.948805 +v 2.713907 1.205618 -1.928023 +v 2.702083 1.334368 -1.930626 +v 1.831320 1.344269 -1.251206 +v -2.260945 0.399211 2.027604 +v -2.181051 -0.441814 2.042546 +v 2.809488 0.450216 -1.928661 +v 2.854570 0.256946 -1.946061 +v -2.423567 -0.369548 2.230060 +v -2.180836 0.422861 1.973894 +v -2.073524 -0.433428 1.968859 +v -1.805897 -0.428247 1.759726 +v -1.887675 0.473899 1.740641 +v -2.358199 -0.380076 2.187846 +v -2.358499 0.188271 2.135889 +v 2.990686 0.501054 -2.063247 +v 3.036054 0.306554 -2.080758 +v 2.665627 1.189319 -1.872442 +v 2.651437 1.343818 -1.875567 +v 2.648270 1.510792 -1.882843 +v 2.930912 1.202118 -2.074323 +v 2.723351 1.236585 -1.913028 +v 2.716951 1.306264 -1.914438 +v 2.917024 1.367804 -2.075984 +v 1.384653 -0.306314 -0.725204 +v 2.724420 1.271928 -1.908258 +v 1.621186 0.490687 -0.975352 +v 1.644965 0.346027 -0.980608 +v 0.465380 0.080433 -0.036070 +v 2.848397 0.355508 -1.916368 +v -2.408185 -0.394890 2.251162 +v 2.942750 1.186138 -2.060817 +v 2.578963 1.033323 -1.763117 +v 2.858250 -0.125781 -1.874424 +v 2.922529 1.183532 -2.043463 +v 2.676959 1.226479 -1.854450 +v 2.669279 1.310094 -1.856141 +v 2.926042 1.385458 -2.062815 +v 2.826142 0.472035 -1.899780 +v 2.880239 0.240111 -1.920660 +v 2.905643 1.384987 -2.045482 +v 3.029535 0.405571 -2.049749 +v 3.300344 0.601920 -2.275317 +v 3.350079 0.388695 -2.294513 +v 2.678242 1.268890 -1.848725 +v 2.945134 1.257227 -2.050923 +v 1.755731 1.246131 -1.122488 +v 1.720327 0.756772 -1.049674 +v 2.940050 1.317871 -2.051532 +v -2.214680 0.228844 2.067608 +v -2.157859 -0.367480 2.078067 +v 1.704158 1.079827 -1.054807 +v -2.127781 0.248308 2.009962 +v -2.051331 -0.359151 2.006140 +v -1.843236 0.290860 1.784206 +v -1.785848 -0.348829 1.798208 +v 1.604892 0.740558 -0.935201 +v 1.693974 -0.161033 -0.921860 +v 3.472397 0.138163 -2.335899 +v 1.873536 1.307570 -1.193691 +v 2.872832 0.358386 -1.885027 +v 2.959859 1.252435 -2.032667 +v 2.953743 1.325390 -2.033399 +v 3.342932 0.497244 -2.260519 +v 2.939822 1.250538 -2.015012 +v 2.829564 0.490591 -1.858224 +v 2.893102 0.218199 -1.882748 +v 2.933641 1.324275 -2.015751 +v 1.701154 -0.246228 -0.905127 +v -0.334995 -0.394882 0.699940 +v 3.318333 0.625178 -2.242012 +v 3.378166 0.368670 -2.265106 +v 3.270936 0.612055 -2.203483 +v 3.331410 0.352798 -2.226824 +v 2.870384 0.194339 -1.832802 +v 2.800672 0.493204 -1.805894 +v 2.706330 1.463624 -1.813588 +v 1.735438 1.031940 -1.015094 +v 2.096574 -0.183066 -1.184359 +v 2.738836 0.478769 -1.744593 +v 2.810966 0.169537 -1.772433 +v 1.857375 -0.219119 -0.990222 +v 2.884402 0.357112 -1.840897 +v 3.369567 0.499255 -2.224210 +v 0.704277 0.096803 -0.109143 +v 3.322719 0.484781 -2.185490 +v 1.668063 0.559071 -0.899985 +v 1.734685 -0.079917 -0.893249 +v 3.503355 0.161047 -2.294336 +v 1.853604 0.697100 -1.057274 +v 0.023839 -0.378671 0.476160 +v 2.742703 1.224204 -1.790460 +v 2.990285 1.243004 -1.985215 +v 1.751320 0.995124 -0.995284 +v 1.762367 0.864072 -0.986848 +v 1.978204 0.694100 -1.137927 +v 1.821889 1.171394 -1.054962 +v 2.860838 0.346753 -1.786883 +v 1.768391 0.914212 -0.976082 +v 2.800102 0.741867 -1.764054 +v 3.047683 0.760667 -1.958810 +v 2.801089 0.327237 -1.724923 +v 2.686153 0.887537 -1.683203 +v 2.915644 -0.066074 -1.774558 +v 1.939461 1.218681 -1.105028 +v 0.380965 -0.356962 0.277840 +v 2.157984 -0.096145 -1.130609 +v 2.171539 -0.180007 -1.133476 +v 1.870303 1.101825 -0.999439 +v 2.768902 0.690672 -1.652155 +v 2.931033 0.013315 -1.716360 +v 2.798614 1.350688 -1.707175 +v 1.895380 1.047986 -0.972059 +v -0.263286 -0.086843 0.817096 +v 1.915261 0.855545 -0.963812 +v 1.995518 1.144847 -1.050030 +v 1.923295 0.929349 -0.946858 +v 0.639411 -0.340042 0.173318 +v 2.048910 0.874962 -1.028633 +v 2.024876 1.087065 -1.024636 +v 0.098227 -0.041243 0.603403 +v 1.153992 0.084424 -0.229250 +v 2.057853 0.957558 -1.005829 +v 2.853774 1.270425 -1.644800 +v 2.691514 -0.001160 -1.386453 +v 2.705070 -0.085024 -1.389320 +v 2.781930 0.015918 -1.458515 +v 2.795485 -0.067945 -1.461382 +v 2.889620 0.961520 -1.629051 +v 3.137201 0.980321 -1.823807 +v 2.880562 1.206416 -1.616926 +v 3.128144 1.225217 -1.811681 +v 2.893827 1.173559 -1.603234 +v 3.141408 1.192360 -1.797990 +v -0.165990 -0.329480 0.922774 +v 2.905606 1.059058 -1.599313 +v 3.153188 1.077859 -1.794069 +v 2.902644 1.136966 -1.595558 +v 3.150226 1.155766 -1.790314 +v 2.906628 1.098234 -1.594232 +v 3.154210 1.117035 -1.788988 +v 0.450756 -0.003152 0.428413 +v 0.893967 -0.321301 0.137348 +v 0.203261 -0.310043 0.717218 +v -0.125580 -0.167821 0.986626 +v 0.712336 0.026704 0.345760 +v -0.090811 -0.262476 1.024542 +v 1.652963 0.059929 -0.335038 +v 0.245682 -0.133976 0.786321 +v 0.568430 -0.286725 0.550539 +v 0.283133 -0.240046 0.827356 +v 2.000346 0.011962 -0.463845 +v 0.611741 -0.102875 0.629863 +v 0.998399 0.048451 0.327453 +v 0.827128 -0.268380 0.491359 +v 1.390944 -0.279641 0.074489 +v 2.438757 0.004155 -0.738633 +v 0.652016 -0.215986 0.675288 +v 2.198852 0.014020 -0.544254 +v 0.870584 -0.078559 0.583979 +v 0.910831 -0.196982 0.637049 +v 1.127571 -0.244822 0.495289 +v 1.185795 -0.053488 0.599517 +v 1.535993 0.058149 0.320863 +v 1.947252 -0.218027 0.037437 +v 1.232465 -0.171199 0.659405 +v 2.293159 -0.159573 -0.040558 +v 2.769338 -0.097283 -0.322594 +v 1.695832 -0.196471 0.528473 +v 2.530709 -0.129037 -0.121828 +v 2.157723 0.063260 0.342121 +v 1.776927 -0.020207 0.662777 +v 1.833764 -0.121263 0.736990 +v 3.351091 0.073201 -0.443243 +v 3.364646 -0.010661 -0.446110 +v 2.948324 -0.003291 -0.110055 +v 2.961879 -0.087154 -0.112922 +v 2.992015 0.002089 -0.060870 +v 3.005570 -0.081773 -0.063737 +v 2.525032 0.038738 0.306061 +v 3.379430 0.074900 -0.358922 +v 3.392986 -0.008964 -0.361790 +v 3.036570 0.045434 0.026135 +v 2.796513 0.049478 0.227779 +v 2.357571 -0.130808 0.595337 +v 2.473462 0.016867 0.762129 +v 2.725617 -0.081249 0.592239 +v 2.544662 -0.059737 0.852031 +v 3.261550 -0.027300 0.308389 +v 3.022995 -0.051504 0.513873 +v 2.851826 0.022984 0.782891 +v 3.407963 0.046255 0.499554 +v 2.923753 -0.024800 0.883847 +v 3.168214 0.041578 0.705848 +v 3.711371 0.106264 0.292980 +v 3.724926 0.022401 0.290113 +v 3.531985 0.072015 0.446671 +v 3.545540 -0.011849 0.443804 +v 3.445588 0.055512 0.520889 +v 3.459143 -0.028351 0.518022 +v 3.563503 0.077601 0.432271 +v 3.577058 -0.006262 0.429404 +v 3.488101 0.014784 0.599486 +v 3.249111 0.000073 0.806963 +vt 0.592295 0.226200 +vt 0.597451 0.733993 +vt 0.582117 0.735152 +vt 0.578489 0.226600 +vt 0.583926 0.737917 +vt 0.580317 0.227579 +vt 0.553525 0.740642 +vt 0.440791 0.740508 +vt 0.450875 0.228401 +vt 0.552974 0.228523 +vt 0.390668 0.737686 +vt 0.405291 0.227370 +vt 0.388859 0.734922 +vt 0.403463 0.226391 +vt 0.371983 0.733724 +vt 0.388098 0.225956 +vt 0.619772 0.907170 +vt 0.592331 0.999817 +vt 0.601330 0.999666 +vt 0.610146 0.909999 +vt 0.574598 0.999907 +vt 0.590330 0.911915 +vt 0.553091 0.999969 +vt 0.566105 0.913419 +vt 0.528748 1.000000 +vt 0.538531 0.914447 +vt 0.502634 1.000000 +vt 0.478247 0.914918 +vt 0.475889 0.999968 +vt 0.508812 0.914955 +vt 0.449684 0.999906 +vt 0.448173 0.914340 +vt 0.425163 0.999816 +vt 0.419902 0.913245 +vt 0.403398 0.999703 +vt 0.394672 0.911681 +vt 0.385339 0.999570 +vt 0.373584 0.909717 +vt 0.375862 0.999397 +vt 0.362094 0.906863 +vt 0.605465 0.740602 +vt 0.597205 0.739049 +vt 0.491046 0.731144 +vt 0.490016 0.901745 +vt 0.489949 0.901745 +vt 0.579978 0.737974 +vt 0.489821 0.901745 +vt 0.558870 0.737116 +vt 0.489665 0.901745 +vt 0.534804 0.736510 +vt 0.489489 0.901745 +vt 0.508831 0.736184 +vt 0.489301 0.901744 +vt 0.482087 0.736152 +vt 0.489205 0.901744 +vt 0.455740 0.736416 +vt 0.430942 0.736963 +vt 0.408777 0.737770 +vt 0.390214 0.738801 +vt 0.379997 0.740333 +vt 0.353656 0.886514 +vt 0.378904 0.730193 +vt 0.361705 0.880889 +vt 0.395023 0.727499 +vt 0.380234 0.876591 +vt 0.415254 0.725377 +vt 0.403440 0.873201 +vt 0.438713 0.723920 +vt 0.430309 0.870869 +vt 0.464376 0.723192 +vt 0.459668 0.869695 +vt 0.477566 0.723232 +vt 0.474742 0.869751 +vt 0.491120 0.723224 +vt 0.490232 0.869731 +vt 0.517777 0.724015 +vt 0.520667 0.870977 +vt 0.543181 0.725530 +vt 0.549643 0.873375 +vt 0.566223 0.727704 +vt 0.575892 0.876824 +vt 0.585895 0.730440 +vt 0.598267 0.881171 +vt 0.611334 0.886821 +vt 0.514919 0.001808 +vt 0.498936 0.000000 +vt 0.513858 0.001846 +vt 0.529721 0.013570 +vt 0.531965 0.013618 +vt 0.556734 0.062512 +vt 0.561107 0.062168 +vt 0.573935 0.111992 +vt 0.579743 0.111218 +vt 0.580242 0.134938 +vt 0.586574 0.134186 +vt 0.586077 0.152110 +vt 0.592909 0.152041 +vt 0.591813 0.178477 +vt 0.599157 0.177963 +vt 0.593139 0.193428 +vt 0.600618 0.192699 +vt 0.592908 0.232476 +vt 0.600324 0.231459 +vt 0.496437 0.231529 +vt 0.508654 0.001888 +vt 0.518859 0.013502 +vt 0.535963 0.062914 +vt 0.546664 0.112914 +vt 0.550579 0.135830 +vt 0.554180 0.152166 +vt 0.557666 0.179076 +vt 0.558422 0.194288 +vt 0.558268 0.233688 +vt 0.501777 0.001902 +vt 0.504546 0.013460 +vt 0.508707 0.063079 +vt 0.510973 0.113312 +vt 0.511780 0.136212 +vt 0.512489 0.152160 +vt 0.513077 0.179318 +vt 0.513104 0.194651 +vt 0.512986 0.234213 +vt 0.498076 0.001898 +vt 0.496856 0.013450 +vt 0.494097 0.063062 +vt 0.491870 0.113289 +vt 0.491019 0.136187 +vt 0.490190 0.152133 +vt 0.489240 0.179289 +vt 0.488883 0.194622 +vt 0.488765 0.234184 +vt 0.490954 0.001867 +vt 0.482078 0.013458 +vt 0.466079 0.062831 +vt 0.455287 0.112805 +vt 0.451272 0.135711 +vt 0.447516 0.152039 +vt 0.443646 0.178940 +vt 0.442563 0.194150 +vt 0.442409 0.233550 +vt 0.485218 0.001812 +vt 0.470208 0.013499 +vt 0.443661 0.062377 +vt 0.426084 0.111816 +vt 0.419558 0.134746 +vt 0.413491 0.151904 +vt 0.407324 0.178257 +vt 0.405675 0.193204 +vt 0.405444 0.232252 +vt 0.483722 0.001771 +vt 0.467140 0.013541 +vt 0.437940 0.062021 +vt 0.418694 0.111026 +vt 0.411548 0.133977 +vt 0.404917 0.151817 +vt 0.398200 0.177723 +vt 0.396421 0.192455 +vt 0.396126 0.231215 +vt 0.482807 0.001091 +vt 0.465246 0.012218 +vt 0.434231 0.060860 +vt 0.413722 0.112734 +vt 0.406278 0.130880 +vt 0.397753 0.154535 +vt 0.390718 0.172336 +vt 0.388811 0.184259 +vt 0.483894 0.000866 +vt 0.467479 0.011696 +vt 0.438359 0.060674 +vt 0.419010 0.113922 +vt 0.412060 0.130257 +vt 0.403381 0.155615 +vt 0.396772 0.170695 +vt 0.394947 0.181718 +vt 0.394122 0.224712 +vt 0.489100 0.000562 +vt 0.478265 0.010987 +vt 0.458692 0.060437 +vt 0.445446 0.115578 +vt 0.440842 0.129428 +vt 0.433493 0.157124 +vt 0.429012 0.168469 +vt 0.427688 0.178256 +vt 0.426709 0.223032 +vt 0.492401 0.000471 +vt 0.485112 0.010775 +vt 0.471652 0.060373 +vt 0.462347 0.116094 +vt 0.459225 0.129185 +vt 0.452993 0.157597 +vt 0.449873 0.167802 +vt 0.448880 0.177211 +vt 0.447853 0.222532 +vt 0.495979 0.000427 +vt 0.492542 0.010672 +vt 0.485745 0.060349 +vt 0.480752 0.116370 +vt 0.479235 0.129074 +vt 0.474359 0.157853 +vt 0.472721 0.167476 +vt 0.472093 0.176692 +vt 0.471042 0.222291 +vt 0.497803 0.000430 +vt 0.496333 0.010680 +vt 0.492949 0.060359 +vt 0.490171 0.116373 +vt 0.489471 0.129091 +vt 0.485358 0.157859 +vt 0.484479 0.167501 +vt 0.484040 0.176724 +vt 0.482990 0.222314 +vt 0.499680 0.000431 +vt 0.500232 0.010681 +vt 0.500355 0.060366 +vt 0.499854 0.116392 +vt 0.499996 0.129099 +vt 0.496658 0.157879 +vt 0.496558 0.167505 +vt 0.496314 0.176721 +vt 0.495264 0.222320 +vt 0.503340 0.000484 +vt 0.507844 0.010802 +vt 0.514842 0.060424 +vt 0.518821 0.116161 +vt 0.520601 0.129259 +vt 0.518915 0.157676 +vt 0.520341 0.167887 +vt 0.520484 0.177297 +vt 0.519458 0.222618 +vt 0.506801 0.000583 +vt 0.515045 0.011031 +vt 0.528575 0.060520 +vt 0.536823 0.115687 +vt 0.540149 0.129546 +vt 0.540157 0.157251 +vt 0.543032 0.168605 +vt 0.543547 0.178394 +vt 0.542568 0.223170 +vt 0.512534 0.000900 +vt 0.526992 0.011767 +vt 0.551432 0.060809 +vt 0.566860 0.114099 +vt 0.572744 0.130449 +vt 0.575967 0.155821 +vt 0.581260 0.170915 +vt 0.582411 0.181941 +vt 0.581586 0.224936 +vt 0.514004 0.001128 +vt 0.530070 0.012295 +vt 0.557398 0.061007 +vt 0.574770 0.112926 +vt 0.581304 0.131089 +vt 0.585745 0.154759 +vt 0.591675 0.172575 +vt 0.593008 0.184503 +vt 0.500194 0.123038 +vt 0.538682 0.145632 +vt 0.499355 0.147686 +vt 0.567425 0.437987 +vt 0.495309 0.437689 +vt 0.572058 0.485965 +vt 0.494594 0.485885 +vt 0.579203 0.530428 +vt 0.494130 0.530571 +vt 0.588071 0.564595 +vt 0.493832 0.570346 +vt 0.603460 0.591429 +vt 0.494064 0.596672 +vt 0.634825 0.640783 +vt 0.495158 0.646017 +vt 0.673396 0.693624 +vt 0.496679 0.696159 +vt 0.692678 0.734348 +vt 0.497801 0.742533 +vt 0.702762 0.758051 +vt 0.498113 0.758399 +vt 0.701606 0.793564 +vt 0.498079 0.793255 +vt 0.493311 0.794017 +vt 0.530328 0.123074 +vt 0.571363 0.144013 +vt 0.627653 0.438535 +vt 0.636786 0.486372 +vt 0.650275 0.530678 +vt 0.666763 0.560227 +vt 0.694688 0.587480 +vt 0.750987 0.636789 +vt 0.820060 0.691789 +vt 0.854194 0.727752 +vt 0.872335 0.757923 +vt 0.870198 0.793953 +vt 0.555251 0.123104 +vt 0.591748 0.143109 +vt 0.665577 0.439237 +vt 0.677585 0.487036 +vt 0.695058 0.531278 +vt 0.716302 0.557996 +vt 0.751975 0.585509 +vt 0.823559 0.634725 +vt 0.911312 0.690971 +vt 0.954422 0.723886 +vt 0.977512 0.758039 +vt 0.974704 0.794354 +vt 0.570653 0.123123 +vt 0.596282 0.143062 +vt 0.674482 0.439878 +vt 0.687203 0.487720 +vt 0.705580 0.531920 +vt 0.727870 0.558106 +vt 0.765162 0.585722 +vt 0.839794 0.634825 +vt 0.931229 0.691232 +vt 0.975934 0.723388 +vt 1.000000 0.758335 +vt 0.996949 0.794648 +vt 0.404437 0.142964 +vt 0.426515 0.122951 +vt 0.573874 0.123126 +vt 0.323231 0.439478 +vt 0.310002 0.487319 +vt 0.291306 0.531610 +vt 0.269108 0.558029 +vt 0.232259 0.585436 +vt 0.158605 0.634392 +vt 0.068392 0.690386 +vt 0.024123 0.722737 +vt 0.000000 0.757160 +vt 0.002277 0.793454 +vt 0.408288 0.143022 +vt 0.329678 0.438833 +vt 0.316870 0.486617 +vt 0.298892 0.530832 +vt 0.277591 0.557845 +vt 0.242362 0.585244 +vt 0.172141 0.634290 +vt 0.086185 0.690163 +vt 0.044209 0.723323 +vt 0.021216 0.756939 +vt 0.023503 0.793219 +vt 0.429735 0.122954 +vt 0.428009 0.143973 +vt 0.365185 0.438218 +vt 0.354927 0.486047 +vt 0.340715 0.530335 +vt 0.323958 0.560190 +vt 0.296482 0.587348 +vt 0.241975 0.636524 +vt 0.175315 0.691197 +vt 0.142962 0.727426 +vt 0.125096 0.757073 +vt 0.126941 0.793066 +vt 0.445138 0.122973 +vt 0.460221 0.145670 +vt 0.423771 0.437812 +vt 0.417791 0.485792 +vt 0.409775 0.530252 +vt 0.400446 0.564744 +vt 0.385513 0.591511 +vt 0.356231 0.640793 +vt 0.320514 0.693380 +vt 0.303405 0.734406 +vt 0.293784 0.757604 +vt 0.294807 0.793079 +vt 0.470061 0.123002 +vt 0.584118 0.143846 +vt 0.652512 0.440323 +vt 0.663585 0.488333 +vt 0.679561 0.533226 +vt 0.698964 0.560878 +vt 0.731472 0.587869 +vt 0.796494 0.637100 +vt 0.876085 0.692426 +vt 0.914822 0.726015 +vt 0.935707 0.758515 +vt 0.932883 0.794633 +vt 0.564354 0.123115 +vt 0.557453 0.145374 +vt 0.603941 0.440767 +vt 0.611404 0.489119 +vt 0.622180 0.535513 +vt 0.635332 0.566338 +vt 0.657480 0.591977 +vt 0.701732 0.641506 +vt 0.755840 0.694578 +vt 0.781940 0.731416 +vt 0.796061 0.758682 +vt 0.793893 0.794463 +vt 0.543740 0.123090 +vt 0.520898 0.147383 +vt 0.537164 0.441133 +vt 0.539680 0.489942 +vt 0.543358 0.538385 +vt 0.547975 0.573543 +vt 0.555980 0.597337 +vt 0.571895 0.647283 +vt 0.591287 0.697315 +vt 0.600266 0.738659 +vt 0.605208 0.758809 +vt 0.604010 0.794167 +vt 0.515597 0.123057 +vt 0.480790 0.147467 +vt 0.463733 0.441064 +vt 0.460823 0.489897 +vt 0.456750 0.538466 +vt 0.452065 0.573899 +vt 0.444570 0.597554 +vt 0.429485 0.647493 +vt 0.410904 0.697284 +vt 0.401280 0.738907 +vt 0.396151 0.758578 +vt 0.396067 0.793912 +vt 0.484791 0.123020 +vt 0.444068 0.145371 +vt 0.396343 0.440538 +vt 0.388469 0.488901 +vt 0.377334 0.535405 +vt 0.364191 0.566484 +vt 0.342521 0.591951 +vt 0.299133 0.641406 +vt 0.245883 0.694154 +vt 0.219396 0.731230 +vt 0.205038 0.757996 +vt 0.206019 0.793754 +vt 0.456648 0.122986 +vt 0.417060 0.143778 +vt 0.346645 0.439977 +vt 0.335120 0.487990 +vt 0.318813 0.532980 +vt 0.299476 0.560871 +vt 0.267422 0.587665 +vt 0.203316 0.636772 +vt 0.124729 0.691714 +vt 0.085988 0.725511 +vt 0.064911 0.757495 +vt 0.066727 0.793592 +vt 0.436035 0.122962 +vt 0.495260 0.736170 +vt 0.500696 0.234197 +vt 0.536496 0.234045 +vt 0.577350 0.233157 +vt 0.495655 0.736171 +vt 0.501053 0.234197 +vt 0.464891 0.233960 +vt 0.422300 0.232972 +vt 0.408534 0.223769 +vt 0.563583 0.223955 +vt 0.367604 0.795692 +vt 0.344548 0.863566 +vt 0.343493 0.862334 +vt 0.368659 0.796923 +vt 0.344327 0.876900 +vt 0.343272 0.875668 +vt 0.145143 0.856433 +vt 0.144088 0.855201 +vt 0.144487 0.796170 +vt 0.143432 0.794939 +vt 0.132013 0.795411 +vt 0.130679 0.853014 +vt 0.129623 0.851782 +vt 0.133068 0.796642 +vt 0.006462 0.836828 +vt 0.005407 0.835597 +vt 0.003936 0.813497 +vt 0.002882 0.812266 +vt 0.005457 0.809612 +vt 0.004402 0.808380 +vt 0.004946 0.796489 +vt 0.003891 0.795258 +vt 0.854563 0.797672 +vt 0.853823 0.852194 +vt 0.854872 0.853763 +vt 0.853514 0.796103 +vt 0.978631 0.836386 +vt 0.979680 0.837955 +vt 0.982018 0.813180 +vt 0.983067 0.814749 +vt 0.980641 0.809310 +vt 0.981690 0.810879 +vt 0.981636 0.796256 +vt 0.982685 0.797825 +vt 0.618972 0.797391 +vt 0.639573 0.862183 +vt 0.640622 0.863752 +vt 0.617923 0.795822 +vt 0.639302 0.875450 +vt 0.640350 0.877019 +vt 0.839233 0.855561 +vt 0.840281 0.857130 +vt 0.842114 0.795606 +vt 0.843162 0.797175 +vt 0.341429 0.892018 +vt 0.354422 0.771751 +vt 0.371180 0.771906 +vt 0.361304 0.757630 +vt 0.375583 0.756290 +vt 0.376061 0.747099 +vt 0.386704 0.746114 +vt 0.396022 0.741918 +vt 0.399519 0.741536 +vt 0.406939 0.741656 +vt 0.407021 0.741651 +vt 0.362783 0.891001 +vt 0.382518 0.772090 +vt 0.385281 0.755640 +vt 0.393806 0.745632 +vt 0.401875 0.741347 +vt 0.407053 0.741648 +vt 0.377126 0.890562 +vt 0.415783 0.773053 +vt 0.413937 0.755087 +vt 0.414119 0.745184 +vt 0.408737 0.741153 +vt 0.407022 0.741639 +vt 0.418673 0.890604 +vt 0.457357 0.774594 +vt 0.449912 0.755475 +vt 0.439090 0.745395 +vt 0.417274 0.741201 +vt 0.406885 0.741627 +vt 0.367889 0.778452 +vt 0.364200 0.897177 +vt 0.470167 0.891713 +vt 0.370122 0.761446 +vt 0.381736 0.748731 +vt 0.398612 0.742335 +vt 0.406817 0.741630 +vt 0.341083 0.776828 +vt 0.346977 0.760159 +vt 0.366096 0.747919 +vt 0.393148 0.742062 +vt 0.406985 0.741639 +vt 0.330950 0.895375 +vt 0.334983 0.775889 +vt 0.341985 0.759566 +vt 0.362746 0.747620 +vt 0.391920 0.741975 +vt 0.407026 0.741643 +vt 0.322792 0.894560 +vt 0.337783 0.774019 +vt 0.345386 0.758625 +vt 0.365129 0.747299 +vt 0.392543 0.741914 +vt 0.407019 0.741650 +vt 0.324143 0.893295 +vt 0.342886 0.773061 +vt 0.350401 0.758202 +vt 0.368571 0.747202 +vt 0.393616 0.741910 +vt 0.406994 0.741653 +vt 0.329152 0.892735 +vt 0.606943 0.892335 +vt 0.578997 0.772154 +vt 0.598183 0.772042 +vt 0.576236 0.756530 +vt 0.592545 0.757906 +vt 0.567870 0.746331 +vt 0.580039 0.747342 +vt 0.558973 0.741727 +vt 0.562967 0.742117 +vt 0.553477 0.741826 +vt 0.553572 0.741831 +vt 0.582516 0.891263 +vt 0.566609 0.772310 +vt 0.565670 0.755855 +vt 0.560098 0.745830 +vt 0.556401 0.741531 +vt 0.553436 0.741823 +vt 0.566831 0.890789 +vt 0.533400 0.773194 +vt 0.537139 0.755234 +vt 0.539725 0.745333 +vt 0.549548 0.741322 +vt 0.553438 0.741813 +vt 0.525261 0.890731 +vt 0.494395 0.774638 +vt 0.503451 0.755539 +vt 0.516203 0.745487 +vt 0.541534 0.741350 +vt 0.553540 0.741802 +vt 0.599293 0.897457 +vt 0.597789 0.778726 +vt 0.476853 0.891721 +vt 0.595602 0.761715 +vt 0.582464 0.748970 +vt 0.563094 0.742531 +vt 0.553620 0.741805 +vt 0.620315 0.777161 +vt 0.614992 0.760479 +vt 0.595561 0.748192 +vt 0.567681 0.742270 +vt 0.553478 0.741814 +vt 0.627351 0.895729 +vt 0.624397 0.776234 +vt 0.618175 0.759896 +vt 0.597681 0.747900 +vt 0.568493 0.742185 +vt 0.553449 0.741818 +vt 0.633134 0.894930 +vt 0.618307 0.774354 +vt 0.611745 0.758943 +vt 0.593228 0.747572 +vt 0.567181 0.742122 +vt 0.553475 0.741825 +vt 0.628051 0.893657 +vt 0.611697 0.773381 +vt 0.605318 0.758506 +vt 0.588819 0.747464 +vt 0.565791 0.742115 +vt 0.553508 0.741827 +vt 0.621374 0.893083 +vt 0.467142 0.741480 +vt 0.460043 0.823727 +vt 0.466255 0.823734 +vt 0.491990 0.741510 +vt 0.491102 0.823764 +vt 0.497314 0.823771 +vt 0.459387 0.936839 +vt 0.481806 0.936865 +vt 0.442062 0.996371 +vt 0.457305 0.996390 +vt 0.441061 0.998017 +vt 0.455880 0.998035 +vt 0.440376 0.996388 +vt 0.454893 0.996405 +vt 0.440171 0.989270 +vt 0.454568 0.989287 +vt 0.440732 0.939254 +vt 0.455138 0.939271 +vt 0.441300 0.928768 +vt 0.455901 0.928785 +vt 0.442019 0.920921 +vt 0.456893 0.920939 +vt 0.442564 0.913924 +vt 0.457640 0.913942 +vt 0.463957 0.747884 +vt 0.487465 0.747912 +vt 0.464844 0.743833 +vt 0.488714 0.743862 +vt 0.549439 0.921434 +vt 0.548851 0.918488 +vt 0.544157 0.921323 +vt 0.551271 0.945406 +vt 0.533127 0.945025 +vt 0.552776 0.983628 +vt 0.528590 0.983120 +vt 0.552915 0.990499 +vt 0.528985 0.989997 +vt 0.552488 0.989209 +vt 0.532597 0.988792 +vt 0.540100 0.920799 +vt 0.519194 0.943227 +vt 0.510019 0.980723 +vt 0.510611 0.987626 +vt 0.517323 0.986820 +vt 0.538358 0.920003 +vt 0.513207 0.940494 +vt 0.502039 0.977080 +vt 0.502715 0.984021 +vt 0.510760 0.983824 +vt 0.539395 0.919149 +vt 0.516770 0.937557 +vt 0.506788 0.973165 +vt 0.507415 0.980148 +vt 0.514666 0.980605 +vt 0.542934 0.918463 +vt 0.528929 0.935204 +vt 0.522994 0.970029 +vt 0.523449 0.977045 +vt 0.527995 0.978025 +vt 0.548028 0.918132 +vt 0.546424 0.934066 +vt 0.546314 0.968512 +vt 0.546522 0.975543 +vt 0.547174 0.976777 +vt 0.553310 0.918243 +vt 0.564569 0.934446 +vt 0.570500 0.969019 +vt 0.570451 0.976045 +vt 0.567066 0.977194 +vt 0.557366 0.918766 +vt 0.578501 0.936245 +vt 0.589071 0.971416 +vt 0.588825 0.978417 +vt 0.582340 0.979166 +vt 0.559110 0.919562 +vt 0.584488 0.938978 +vt 0.597051 0.975059 +vt 0.596721 0.982022 +vt 0.588903 0.982162 +vt 0.558072 0.920417 +vt 0.580925 0.941915 +vt 0.592302 0.978974 +vt 0.592022 0.985895 +vt 0.584996 0.985382 +vt 0.554533 0.921102 +vt 0.568767 0.944267 +vt 0.576096 0.982110 +vt 0.575988 0.988998 +vt 0.571668 0.987961 +vt 0.424145 0.921277 +vt 0.423693 0.918338 +vt 0.418870 0.920933 +vt 0.423822 0.945229 +vt 0.405702 0.944048 +vt 0.421388 0.983437 +vt 0.397235 0.981863 +vt 0.420772 0.990308 +vt 0.396875 0.988751 +vt 0.420346 0.989024 +vt 0.400481 0.987729 +vt 0.414819 0.920239 +vt 0.391787 0.941664 +vt 0.378687 0.978685 +vt 0.378524 0.985607 +vt 0.385227 0.985116 +vt 0.413078 0.919381 +vt 0.385807 0.938716 +vt 0.370716 0.974756 +vt 0.370638 0.981719 +vt 0.378671 0.981884 +vt 0.414113 0.918588 +vt 0.389363 0.935994 +vt 0.375457 0.971128 +vt 0.375328 0.978129 +vt 0.382570 0.978900 +vt 0.417648 0.918074 +vt 0.401504 0.934227 +vt 0.391639 0.968773 +vt 0.391339 0.975799 +vt 0.395879 0.976963 +vt 0.422734 0.917975 +vt 0.418975 0.933889 +vt 0.414927 0.968321 +vt 0.414380 0.975353 +vt 0.415032 0.976592 +vt 0.428009 0.918319 +vt 0.437095 0.935070 +vt 0.439080 0.969896 +vt 0.438277 0.976910 +vt 0.434897 0.977887 +vt 0.432060 0.919013 +vt 0.451010 0.937454 +vt 0.457627 0.973073 +vt 0.456628 0.980054 +vt 0.450151 0.980500 +vt 0.433801 0.919872 +vt 0.456990 0.940402 +vt 0.465599 0.977003 +vt 0.464515 0.983942 +vt 0.456707 0.983732 +vt 0.432766 0.920664 +vt 0.453434 0.943124 +vt 0.460858 0.980631 +vt 0.459824 0.987532 +vt 0.452808 0.986716 +vt 0.429231 0.921178 +vt 0.441293 0.944891 +vt 0.444676 0.982987 +vt 0.443813 0.989862 +vt 0.439499 0.988653 +vt 0.489178 0.904955 +vt 0.478920 0.903724 +vt 0.488659 0.905860 +vt 0.513457 0.924223 +vt 0.511674 0.927332 +vt 0.523738 0.959386 +vt 0.521362 0.963531 +vt 0.522949 0.966121 +vt 0.520598 0.970220 +vt 0.515067 0.966111 +vt 0.513113 0.969519 +vt 0.486649 0.906626 +vt 0.504771 0.929961 +vt 0.512160 0.967035 +vt 0.511494 0.973688 +vt 0.505545 0.972402 +vt 0.483455 0.907135 +vt 0.493798 0.931710 +vt 0.497535 0.969367 +vt 0.497023 0.975995 +vt 0.493517 0.974319 +vt 0.479562 0.907310 +vt 0.480427 0.932313 +vt 0.479712 0.970170 +vt 0.479389 0.976790 +vt 0.478859 0.974980 +vt 0.475564 0.907126 +vt 0.466693 0.931678 +vt 0.461405 0.969323 +vt 0.461277 0.975952 +vt 0.463802 0.974284 +vt 0.472068 0.906608 +vt 0.454687 0.929901 +vt 0.445402 0.966955 +vt 0.445442 0.973609 +vt 0.450640 0.972336 +vt 0.469608 0.905838 +vt 0.446236 0.927254 +vt 0.434137 0.963426 +vt 0.434297 0.970118 +vt 0.441375 0.969434 +vt 0.468558 0.904931 +vt 0.442627 0.924138 +vt 0.429327 0.959274 +vt 0.429538 0.966009 +vt 0.437419 0.966018 +vt 0.469077 0.904026 +vt 0.444410 0.921030 +vt 0.431703 0.955130 +vt 0.431889 0.961909 +vt 0.439374 0.962610 +vt 0.471086 0.903260 +vt 0.451313 0.918400 +vt 0.440904 0.951625 +vt 0.440993 0.958442 +vt 0.446941 0.959728 +vt 0.474281 0.902751 +vt 0.462285 0.916651 +vt 0.455530 0.949294 +vt 0.455463 0.956135 +vt 0.458970 0.957810 +vt 0.478174 0.902575 +vt 0.475656 0.916048 +vt 0.473353 0.948490 +vt 0.473097 0.955340 +vt 0.473628 0.957150 +vt 0.482172 0.902760 +vt 0.489391 0.916684 +vt 0.491660 0.949337 +vt 0.491210 0.956177 +vt 0.488685 0.957846 +vt 0.485667 0.903278 +vt 0.501397 0.918460 +vt 0.507663 0.951705 +vt 0.507044 0.958521 +vt 0.501847 0.959794 +vt 0.488128 0.904048 +vt 0.509848 0.921108 +vt 0.518928 0.955234 +vt 0.518190 0.962012 +vt 0.511111 0.962696 +vt 0.490402 0.906636 +vt 0.478826 0.907307 +vt 0.488446 0.905821 +vt 0.501508 0.904625 +vt 0.497682 0.903031 +vt 0.513572 0.898392 +vt 0.507734 0.895959 +vt 0.520474 0.898400 +vt 0.513468 0.895480 +vt 0.527756 0.896328 +vt 0.519527 0.892898 +vt 0.532641 0.890349 +vt 0.523612 0.886586 +vt 0.534751 0.880504 +vt 0.525409 0.876611 +vt 0.483918 0.905221 +vt 0.488823 0.901856 +vt 0.494212 0.894166 +vt 0.497242 0.893329 +vt 0.500470 0.890372 +vt 0.502703 0.883814 +vt 0.503775 0.873743 +vt 0.478030 0.904996 +vt 0.477305 0.901416 +vt 0.476632 0.893495 +vt 0.476145 0.892524 +vt 0.475691 0.889426 +vt 0.475516 0.882776 +vt 0.475646 0.872669 +vt 0.472360 0.905207 +vt 0.466213 0.901829 +vt 0.459702 0.894125 +vt 0.455830 0.893280 +vt 0.451831 0.890314 +vt 0.449337 0.883750 +vt 0.448558 0.873677 +vt 0.468427 0.905797 +vt 0.458520 0.902984 +vt 0.447960 0.895887 +vt 0.441739 0.895394 +vt 0.435282 0.892798 +vt 0.431179 0.886476 +vt 0.429771 0.876497 +vt 0.467286 0.906609 +vt 0.456287 0.904571 +vt 0.444552 0.898310 +vt 0.437650 0.898301 +vt 0.430478 0.896212 +vt 0.425909 0.890222 +vt 0.424318 0.880373 +vt 0.469241 0.907423 +vt 0.460112 0.906165 +vt 0.450390 0.900743 +vt 0.444656 0.901221 +vt 0.438707 0.899641 +vt 0.434938 0.893984 +vt 0.433660 0.884266 +vt 0.473770 0.908024 +vt 0.468971 0.907340 +vt 0.463912 0.902535 +vt 0.460882 0.903372 +vt 0.457764 0.902168 +vt 0.455847 0.896756 +vt 0.455294 0.887134 +vt 0.479658 0.908248 +vt 0.480490 0.907779 +vt 0.481492 0.903207 +vt 0.481979 0.904178 +vt 0.482542 0.903114 +vt 0.483033 0.897794 +vt 0.483423 0.888208 +vt 0.485328 0.908037 +vt 0.491581 0.907367 +vt 0.498422 0.902577 +vt 0.502294 0.903422 +vt 0.506403 0.902226 +vt 0.509212 0.896820 +vt 0.510510 0.887200 +vt 0.489260 0.907447 +vt 0.499275 0.906212 +vt 0.510164 0.900814 +vt 0.516385 0.901307 +vt 0.522952 0.899742 +vt 0.527370 0.894095 +vt 0.529298 0.884380 +vt 0.435164 0.921226 +vt 0.423586 0.921892 +vt 0.433212 0.920281 +vt 0.446278 0.919232 +vt 0.442459 0.917382 +vt 0.458366 0.913050 +vt 0.452538 0.910227 +vt 0.465268 0.913058 +vt 0.458275 0.909671 +vt 0.472557 0.911003 +vt 0.464343 0.907025 +vt 0.477464 0.905073 +vt 0.468453 0.900708 +vt 0.479612 0.895309 +vt 0.470288 0.890792 +vt 0.428687 0.919585 +vt 0.433606 0.916021 +vt 0.439025 0.908150 +vt 0.442059 0.907178 +vt 0.445298 0.904097 +vt 0.447556 0.897496 +vt 0.448667 0.887469 +vt 0.422799 0.919325 +vt 0.422089 0.915513 +vt 0.421447 0.907374 +vt 0.420965 0.906248 +vt 0.420524 0.903004 +vt 0.420375 0.896296 +vt 0.420542 0.886228 +vt 0.417129 0.919572 +vt 0.410995 0.915994 +vt 0.404515 0.908109 +vt 0.400647 0.907129 +vt 0.396659 0.904039 +vt 0.394191 0.897432 +vt 0.393450 0.887403 +vt 0.413194 0.920257 +vt 0.403297 0.917335 +vt 0.392764 0.910156 +vt 0.386546 0.909585 +vt 0.380099 0.906924 +vt 0.376020 0.900598 +vt 0.374650 0.890678 +vt 0.412048 0.921199 +vt 0.401056 0.919178 +vt 0.389345 0.912967 +vt 0.382443 0.912959 +vt 0.375279 0.910887 +vt 0.370733 0.904945 +vt 0.369179 0.895177 +vt 0.414000 0.922144 +vt 0.404875 0.921027 +vt 0.395173 0.915790 +vt 0.389436 0.916346 +vt 0.383493 0.914865 +vt 0.379745 0.909310 +vt 0.378503 0.899693 +vt 0.418526 0.922840 +vt 0.413728 0.922388 +vt 0.408686 0.917867 +vt 0.405652 0.918839 +vt 0.402538 0.917793 +vt 0.400641 0.912523 +vt 0.400124 0.903017 +vt 0.424413 0.923099 +vt 0.425245 0.922896 +vt 0.426264 0.918643 +vt 0.426745 0.919770 +vt 0.427312 0.918886 +vt 0.427823 0.913722 +vt 0.428249 0.904258 +vt 0.430084 0.922854 +vt 0.436339 0.922415 +vt 0.443196 0.917909 +vt 0.447065 0.918889 +vt 0.451177 0.917851 +vt 0.454007 0.912586 +vt 0.455341 0.903083 +vt 0.434019 0.922168 +vt 0.444037 0.921074 +vt 0.454947 0.915862 +vt 0.461165 0.916432 +vt 0.467738 0.914966 +vt 0.472177 0.909421 +vt 0.474141 0.899807 +vt 0.560322 0.921376 +vt 0.548743 0.922041 +vt 0.558370 0.920430 +vt 0.571436 0.919381 +vt 0.567617 0.917532 +vt 0.583524 0.913199 +vt 0.577696 0.910376 +vt 0.590426 0.913208 +vt 0.583432 0.909820 +vt 0.597715 0.911152 +vt 0.589501 0.907174 +vt 0.602622 0.905222 +vt 0.593610 0.900857 +vt 0.604770 0.895458 +vt 0.595446 0.890942 +vt 0.553844 0.919735 +vt 0.558764 0.916170 +vt 0.564183 0.908299 +vt 0.567217 0.907327 +vt 0.570456 0.904246 +vt 0.572714 0.897645 +vt 0.573824 0.887618 +vt 0.547957 0.919475 +vt 0.547247 0.915662 +vt 0.546605 0.907524 +vt 0.546123 0.906397 +vt 0.545682 0.903153 +vt 0.545532 0.896446 +vt 0.545700 0.886378 +vt 0.542286 0.919721 +vt 0.536153 0.916144 +vt 0.529672 0.908258 +vt 0.525804 0.907278 +vt 0.521817 0.904188 +vt 0.519348 0.897581 +vt 0.518608 0.887552 +vt 0.538351 0.920406 +vt 0.528455 0.917485 +vt 0.517922 0.910305 +vt 0.511704 0.909735 +vt 0.505256 0.907073 +vt 0.501178 0.900747 +vt 0.499808 0.890828 +vt 0.537206 0.921348 +vt 0.526215 0.919327 +vt 0.514503 0.913117 +vt 0.507601 0.913109 +vt 0.500437 0.911036 +vt 0.495891 0.905095 +vt 0.494337 0.895326 +vt 0.539158 0.922293 +vt 0.530033 0.921176 +vt 0.520331 0.915940 +vt 0.514594 0.916496 +vt 0.508651 0.915014 +vt 0.504902 0.909460 +vt 0.503661 0.899843 +vt 0.543684 0.922989 +vt 0.538886 0.922537 +vt 0.533844 0.918017 +vt 0.530810 0.918989 +vt 0.527696 0.917942 +vt 0.525799 0.912672 +vt 0.525282 0.903166 +vt 0.549571 0.923249 +vt 0.550403 0.923045 +vt 0.551422 0.918792 +vt 0.551903 0.919919 +vt 0.552470 0.919035 +vt 0.552980 0.913871 +vt 0.553407 0.904407 +vt 0.555241 0.923003 +vt 0.561497 0.922564 +vt 0.568354 0.918058 +vt 0.572222 0.919038 +vt 0.576335 0.918000 +vt 0.579165 0.912735 +vt 0.580499 0.903232 +vt 0.559177 0.922317 +vt 0.569195 0.921223 +vt 0.580104 0.916011 +vt 0.586323 0.916581 +vt 0.592896 0.915115 +vt 0.597334 0.909570 +vt 0.599299 0.899957 +vt 0.559044 0.903560 +vt 0.555560 0.904061 +vt 0.557240 0.903502 +vt 0.562383 0.902441 +vt 0.558853 0.902327 +vt 0.565998 0.899330 +vt 0.560611 0.899155 +vt 0.568167 0.890833 +vt 0.561703 0.890623 +vt 0.555180 0.903480 +vt 0.554825 0.902283 +vt 0.554462 0.899089 +vt 0.554324 0.890544 +vt 0.553180 0.903497 +vt 0.550910 0.902317 +vt 0.548487 0.899141 +vt 0.547155 0.890606 +vt 0.551541 0.903552 +vt 0.547705 0.902423 +vt 0.543596 0.899303 +vt 0.541285 0.890801 +vt 0.550515 0.903635 +vt 0.545699 0.902586 +vt 0.540533 0.899551 +vt 0.537609 0.891098 +vt 0.550258 0.903734 +vt 0.545195 0.902780 +vt 0.539764 0.899847 +vt 0.536687 0.891453 +vt 0.550808 0.903834 +vt 0.546272 0.902975 +vt 0.541408 0.900146 +vt 0.538658 0.891812 +vt 0.552082 0.903920 +vt 0.548764 0.903143 +vt 0.545212 0.900402 +vt 0.543224 0.892119 +vt 0.553886 0.903978 +vt 0.552293 0.903257 +vt 0.550598 0.900576 +vt 0.549688 0.892329 +vt 0.555946 0.904000 +vt 0.556322 0.903301 +vt 0.556747 0.900643 +vt 0.557066 0.892408 +vt 0.557947 0.903983 +vt 0.560237 0.903267 +vt 0.562722 0.900591 +vt 0.564236 0.892346 +vt 0.559585 0.903929 +vt 0.563441 0.903161 +vt 0.567613 0.900428 +vt 0.570106 0.892151 +vt 0.560611 0.903845 +vt 0.565448 0.902998 +vt 0.570676 0.900181 +vt 0.573781 0.891854 +vt 0.560868 0.903746 +vt 0.565951 0.902804 +vt 0.571445 0.899885 +vt 0.574704 0.891499 +vt 0.560318 0.903646 +vt 0.564875 0.902609 +vt 0.569802 0.899586 +vt 0.572732 0.891140 +vt 0.397743 0.903368 +vt 0.394259 0.903868 +vt 0.395939 0.903310 +vt 0.401082 0.902248 +vt 0.397553 0.902134 +vt 0.404697 0.899137 +vt 0.399311 0.898963 +vt 0.406867 0.890641 +vt 0.400403 0.890431 +vt 0.393880 0.903288 +vt 0.393524 0.902090 +vt 0.393162 0.898897 +vt 0.393024 0.890351 +vt 0.391879 0.903305 +vt 0.389610 0.902124 +vt 0.387187 0.898948 +vt 0.385854 0.890414 +vt 0.390241 0.903359 +vt 0.386405 0.902231 +vt 0.382296 0.899111 +vt 0.379985 0.890608 +vt 0.389215 0.903442 +vt 0.384398 0.902393 +vt 0.379233 0.899359 +vt 0.376309 0.890906 +vt 0.388958 0.903541 +vt 0.383895 0.902587 +vt 0.378464 0.899655 +vt 0.375387 0.891261 +vt 0.389508 0.903641 +vt 0.384971 0.902783 +vt 0.380107 0.899953 +vt 0.377358 0.891619 +vt 0.390782 0.903727 +vt 0.387464 0.902951 +vt 0.383912 0.900209 +vt 0.381923 0.891927 +vt 0.392586 0.903786 +vt 0.390993 0.903065 +vt 0.389298 0.900384 +vt 0.388387 0.892136 +vt 0.394645 0.903808 +vt 0.395022 0.903108 +vt 0.395447 0.900450 +vt 0.395766 0.892216 +vt 0.396646 0.903790 +vt 0.398936 0.903074 +vt 0.401422 0.900398 +vt 0.402936 0.892153 +vt 0.398285 0.903736 +vt 0.402141 0.902968 +vt 0.406313 0.900236 +vt 0.408806 0.891959 +vt 0.399310 0.903653 +vt 0.404148 0.902806 +vt 0.409376 0.899988 +vt 0.412481 0.891661 +vt 0.399568 0.903554 +vt 0.404651 0.902612 +vt 0.410145 0.899692 +vt 0.413404 0.891306 +vt 0.399017 0.903454 +vt 0.403575 0.902416 +vt 0.408502 0.899393 +vt 0.411432 0.890948 +vt 0.560050 0.895619 +vt 0.598903 0.933649 +vt 0.559659 0.931810 +vt 0.626960 0.931921 +vt 0.632743 0.931121 +vt 0.633550 0.930793 +vt 0.633940 0.894602 +vt 0.632944 0.930462 +vt 0.633334 0.894271 +vt 0.630952 0.930143 +vt 0.631342 0.893951 +vt 0.627661 0.929849 +vt 0.589170 0.929516 +vt 0.589561 0.893325 +vt 0.398164 0.895426 +vt 0.363809 0.933369 +vt 0.397773 0.931617 +vt 0.330560 0.931567 +vt 0.322401 0.930751 +vt 0.321016 0.894228 +vt 0.320625 0.930420 +vt 0.320649 0.893898 +vt 0.320258 0.930089 +vt 0.321706 0.893582 +vt 0.321316 0.929773 +vt 0.323752 0.929486 +vt 0.361788 0.893053 +vt 0.361398 0.929244 +vt 0.559656 0.902097 +vt 0.555589 0.901346 +vt 0.557317 0.902165 +vt 0.573965 0.927557 +vt 0.563255 0.927865 +vt 0.573734 0.930613 +vt 0.563137 0.930918 +vt 0.570621 0.930463 +vt 0.561812 0.930717 +vt 0.554520 0.902163 +vt 0.550449 0.927858 +vt 0.550467 0.930911 +vt 0.551280 0.930711 +vt 0.552015 0.902093 +vt 0.538979 0.927538 +vt 0.539119 0.930594 +vt 0.541846 0.930447 +vt 0.550473 0.901973 +vt 0.531919 0.926990 +vt 0.532133 0.930052 +vt 0.536040 0.929997 +vt 0.550307 0.901836 +vt 0.531159 0.926362 +vt 0.531382 0.929430 +vt 0.535415 0.929480 +vt 0.551562 0.901718 +vt 0.536905 0.925821 +vt 0.537067 0.928895 +vt 0.540141 0.929035 +vt 0.553901 0.901651 +vt 0.547615 0.925513 +vt 0.547664 0.928591 +vt 0.548949 0.928782 +vt 0.556698 0.901652 +vt 0.560422 0.925520 +vt 0.560334 0.928598 +vt 0.559482 0.928788 +vt 0.559203 0.901723 +vt 0.571891 0.925841 +vt 0.571682 0.928915 +vt 0.568915 0.929052 +vt 0.560745 0.901842 +vt 0.578951 0.926388 +vt 0.578668 0.929457 +vt 0.574722 0.929502 +vt 0.560911 0.901979 +vt 0.579711 0.927017 +vt 0.579419 0.930079 +vt 0.575346 0.930019 +vt 0.398356 0.901905 +vt 0.394289 0.901154 +vt 0.396017 0.901972 +vt 0.412665 0.927365 +vt 0.401954 0.927673 +vt 0.412434 0.930421 +vt 0.401837 0.930726 +vt 0.409321 0.930271 +vt 0.400512 0.930524 +vt 0.394258 0.903992 +vt 0.393220 0.901971 +vt 0.389148 0.927666 +vt 0.389167 0.930719 +vt 0.389979 0.930518 +vt 0.390714 0.901901 +vt 0.377679 0.927345 +vt 0.377819 0.930402 +vt 0.380546 0.930255 +vt 0.389172 0.901781 +vt 0.370618 0.926798 +vt 0.370833 0.929860 +vt 0.374739 0.929804 +vt 0.389006 0.901644 +vt 0.369859 0.926169 +vt 0.370082 0.929238 +vt 0.374115 0.929287 +vt 0.390261 0.901526 +vt 0.375605 0.925629 +vt 0.375767 0.928703 +vt 0.378840 0.928843 +vt 0.392601 0.901458 +vt 0.386315 0.925321 +vt 0.386364 0.928398 +vt 0.387649 0.928590 +vt 0.395398 0.901460 +vt 0.399121 0.925328 +vt 0.399034 0.928406 +vt 0.398181 0.928596 +vt 0.397903 0.901530 +vt 0.410591 0.925648 +vt 0.410382 0.928723 +vt 0.407615 0.928859 +vt 0.399445 0.901650 +vt 0.417651 0.926196 +vt 0.417368 0.929265 +vt 0.413422 0.929310 +vt 0.399611 0.901787 +vt 0.418410 0.926824 +vt 0.418119 0.929886 +vt 0.414046 0.929826 +vn 0.011353 0.401349 0.915830 +vn 0.382275 0.411969 -0.827082 +vn -0.579608 0.385968 -0.717673 +vn -0.581500 0.379101 -0.719810 +vn -0.578936 0.391400 -0.715262 +vn -0.587512 0.353526 -0.727866 +vn -0.281625 0.919126 -0.275399 +vn 0.057253 0.984527 0.165563 +vn 0.071383 0.980926 0.180639 +vn -0.296457 0.908780 -0.293558 +vn 0.474380 0.596789 0.647114 +vn 0.491165 0.565630 0.662404 +vn 0.513932 0.515061 0.685965 +vn 0.517106 0.508774 0.688223 +vn 0.275857 0.401013 -0.873531 +vn -0.191260 0.288827 -0.938047 +vn -0.394971 -0.383221 0.834925 +vn 0.387310 -0.917112 0.094089 +vn 0.623280 0.340129 0.704123 +vn 0.167821 -0.764855 0.621906 +vn 0.346049 -0.937437 -0.037507 +vn 0.028108 -0.899045 0.436933 +vn 0.351848 -0.931425 -0.092898 +vn -0.021699 -0.935636 0.352214 +vn 0.354595 -0.925047 -0.135960 +vn -0.066134 -0.957091 0.282022 +vn 0.350780 -0.921049 -0.169012 +vn 0.518418 -0.764580 -0.382855 +vn 0.338420 -0.920835 -0.193701 +vn 0.202460 -0.979156 -0.015046 +vn 0.316080 -0.924802 -0.211646 +vn 0.010743 -0.996918 -0.077364 +vn 0.282449 -0.932432 -0.225257 +vn -0.263527 -0.963652 0.043672 +vn 0.235603 -0.942106 -0.238563 +vn -0.338664 -0.940489 -0.026948 +vn 0.130589 -0.939299 -0.317209 +vn -0.497787 -0.838588 -0.221198 +vn -0.419324 0.243233 -0.874599 +vn -0.876553 -0.425245 0.225349 +vn 0.628407 0.047426 -0.776421 +vn 0.780297 -0.115146 -0.614673 +vn 0.783563 0.069918 -0.617328 +vn -0.748405 -0.260353 0.609973 +vn -0.749504 -0.261116 0.608264 +vn 0.753014 -0.055422 -0.655629 +vn -0.749474 -0.261025 0.608386 +vn 0.758538 -0.024628 -0.651143 +vn -0.749535 -0.261055 0.608264 +vn 0.767571 -0.000092 -0.640919 +vn -0.749565 -0.261055 0.608234 +vn 0.611438 -0.665090 -0.428663 +vn -0.751213 -0.254250 0.609088 +vn 0.407697 -0.867550 -0.284768 +vn -0.752129 -0.255257 0.607532 +vn 0.356670 -0.868801 -0.343394 +vn 0.296243 -0.863887 -0.407300 +vn 0.220618 -0.847133 -0.483383 +vn 0.021393 -0.758263 -0.651570 +vn -0.141331 -0.391461 -0.909268 +vn -0.187475 -0.241737 0.952055 +vn 0.837397 0.084780 -0.539933 +vn -0.023103 -0.413434 0.910215 +vn 0.824702 0.075686 -0.560442 +vn -0.095370 -0.585253 0.805200 +vn 0.812677 0.068239 -0.578692 +vn -0.154118 -0.691580 0.705618 +vn 0.800623 0.065004 -0.595599 +vn -0.233772 -0.776574 0.584979 +vn 0.788965 0.064425 -0.611011 +vn -0.333689 -0.831782 0.443556 +vn 0.785150 0.052644 -0.617023 +vn -0.415937 -0.817103 0.399091 +vn 0.779382 0.063265 -0.623310 +vn -0.437086 -0.843745 0.311441 +vn 0.767327 0.061159 -0.638295 +vn -0.553789 -0.813898 0.175604 +vn 0.753685 0.061495 -0.654317 +vn -0.658071 -0.750481 0.060671 +vn 0.738456 0.065859 -0.671041 +vn -0.748985 -0.661794 -0.031587 +vn 0.720969 0.071505 -0.689230 +vn -0.847560 -0.510117 -0.146214 +vn -0.934355 -0.298257 -0.194861 +vn -0.388806 -0.092868 0.916593 +vn -0.783807 -0.067476 0.617298 +vn -0.417402 -0.324046 0.848964 +vn 0.045289 -0.536576 0.842616 +vn 0.101627 -0.139500 0.984985 +vn 0.268532 -0.648549 0.712180 +vn 0.342967 -0.179662 0.921964 +vn 0.327982 -0.690237 0.644917 +vn 0.425520 -0.197241 0.883145 +vn 0.333811 -0.702963 0.627979 +vn 0.428297 -0.209754 0.878933 +vn 0.353496 -0.704642 0.615192 +vn 0.454268 -0.208472 0.866115 +vn 0.400616 -0.719047 0.567858 +vn 0.518052 -0.221412 0.826167 +vn 0.455519 -0.674062 0.581439 +vn 0.540757 -0.377819 0.751518 +vn -0.056063 -0.389721 0.919187 +vn -0.071047 -0.408887 0.909787 +vn -0.799554 -0.060579 0.597491 +vn -0.499863 -0.502060 0.705710 +vn -0.068178 -0.820673 0.567278 +vn 0.131443 -0.908658 0.396252 +vn 0.175726 -0.927091 0.331004 +vn 0.185553 -0.931791 0.311930 +vn 0.202643 -0.933988 0.294198 +vn 0.229926 -0.937773 0.260079 +vn 0.249245 -0.927580 0.278268 +vn -0.605335 -0.408643 0.683035 +vn -0.580706 -0.562334 0.588610 +vn -0.185675 -0.914670 0.358959 +vn 0.015992 -0.980255 0.196997 +vn 0.065188 -0.986755 0.148350 +vn 0.079196 -0.987762 0.134129 +vn 0.098361 -0.988250 0.116916 +vn 0.122410 -0.988006 0.093783 +vn 0.182073 -0.973327 0.139500 +vn -0.653920 -0.466628 0.595477 +vn -0.661611 -0.570849 0.486160 +vn -0.310678 -0.929106 0.200537 +vn -0.097995 -0.993866 0.051149 +vn -0.037843 -0.999146 0.014679 +vn -0.021607 -0.999725 0.005341 +vn 0.000305 -0.999939 -0.008972 +vn 0.045381 -0.998901 0.010651 +vn 0.056917 0.996734 0.057009 +vn -0.761834 -0.063967 0.644581 +vn -0.760979 -0.529954 0.374187 +vn -0.492935 -0.869564 0.028474 +vn -0.268410 -0.956328 -0.115513 +vn -0.190405 -0.971160 -0.143376 +vn -0.172277 -0.974181 -0.145756 +vn -0.149602 -0.976074 -0.157720 +vn -0.080416 -0.988525 -0.127750 +vn 0.157567 0.963073 0.218177 +vn -0.774438 -0.080844 0.627430 +vn -0.895108 -0.376080 0.239387 +vn -0.758873 -0.626179 -0.178747 +vn -0.565905 -0.744591 -0.353954 +vn -0.478896 -0.784600 -0.393689 +vn -0.462539 -0.795160 -0.392071 +vn -0.446303 -0.797143 -0.406629 +vn -0.383801 -0.811182 -0.441176 +vn -0.569842 -0.402722 -0.716269 +vn -0.867214 -0.482040 -0.124546 +vn -0.972625 -0.160802 0.167699 +vn -0.913907 -0.254952 -0.315836 +vn -0.789666 -0.307993 -0.530595 +vn -0.731681 -0.333262 -0.594592 +vn -0.719108 -0.343425 -0.604053 +vn -0.711142 -0.341990 -0.614215 +vn -0.645924 -0.361064 -0.672597 +vn -0.589465 -0.359386 -0.723411 +vn -0.558916 -0.355724 -0.749016 +vn -0.984649 -0.017029 0.173559 +vn -0.950407 0.063387 -0.304422 +vn -0.841395 0.103824 -0.530290 +vn -0.806238 0.117710 -0.579699 +vn -0.796869 0.065188 -0.600574 +vn -0.815058 0.082797 -0.573412 +vn -0.747459 0.052217 -0.662221 +vn -0.662618 0.054933 -0.746910 +vn -0.946440 0.184942 0.264534 +vn -0.901151 0.405866 -0.152165 +vn -0.791284 0.484329 -0.373119 +vn -0.758507 0.497940 -0.420301 +vn -0.832942 0.412763 -0.368511 +vn -0.853053 0.386303 -0.350780 +vn -0.759667 0.372906 -0.532762 +vn -0.660024 0.386639 -0.644063 +vn -0.246498 0.354472 -0.901975 +vn -0.869778 0.307108 0.386151 +vn -0.768059 0.638356 0.050264 +vn -0.649892 0.745659 -0.146855 +vn -0.625477 0.757378 -0.187414 +vn -0.755150 0.653676 -0.049348 +vn -0.807489 0.588000 -0.046693 +vn -0.708548 0.652181 -0.269387 +vn -0.584002 0.694327 -0.420484 +vn -0.127415 0.687521 -0.714866 +vn -0.815638 0.347392 0.462600 +vn -0.668355 0.719535 0.188513 +vn -0.535874 0.844081 0.017426 +vn -0.510453 0.859676 -0.017945 +vn -0.661184 0.738060 0.134465 +vn -0.726676 0.668844 0.156682 +vn -0.613208 0.788659 -0.044343 +vn -0.469069 0.859127 -0.204505 +vn -0.005982 0.836024 -0.548601 +vn -0.769311 0.368267 0.521989 +vn -0.581652 0.755638 0.301035 +vn -0.433180 0.887845 0.155126 +vn -0.404614 0.905606 0.127018 +vn -0.570086 0.773553 0.276742 +vn -0.631184 0.706412 0.320200 +vn -0.491775 0.855464 0.162236 +vn -0.328074 0.944578 0.010163 +vn 0.136052 0.927732 -0.347453 +vn -0.751610 0.359294 0.553117 +vn -0.543962 0.760521 0.354472 +vn -0.385083 0.896725 0.218116 +vn -0.352306 0.916196 0.190802 +vn -0.517808 0.788629 0.331462 +vn -0.585437 0.710044 0.391247 +vn -0.428938 0.866573 0.255043 +vn -0.254402 0.960753 0.110477 +vn 0.239692 0.931913 -0.272164 +vn -0.726463 0.373699 0.576678 +vn -0.501083 0.765160 0.404187 +vn -0.335337 0.899319 0.280557 +vn -0.301431 0.917936 0.257851 +vn -0.474563 0.785058 0.398083 +vn -0.523942 0.719474 0.455824 +vn -0.352763 0.870785 0.342387 +vn -0.173925 0.962493 0.208197 +vn -0.035707 0.993988 0.103244 +vn -0.678487 0.364696 0.637684 +vn -0.410749 0.749992 0.518418 +vn -0.224158 0.880764 0.417066 +vn -0.182409 0.898740 0.398663 +vn -0.354961 0.774987 0.522843 +vn -0.385052 0.709708 0.589892 +vn -0.180914 0.837123 0.516190 +vn -0.016968 0.917447 0.397443 +vn -0.462996 0.684591 0.562975 +vn -0.612934 0.339244 0.713553 +vn -0.294046 0.693960 0.657216 +vn -0.084872 0.811884 0.577593 +vn -0.034639 0.826319 0.562120 +vn -0.180486 0.720573 0.669454 +vn -0.199011 0.655660 0.728324 +vn 0.022217 0.734397 0.678335 +vn 0.145665 0.812983 0.563738 +vn -0.714560 0.223823 0.662770 +vn -0.504288 0.240455 0.829341 +vn -0.113254 0.500290 0.858394 +vn 0.119724 0.592853 0.796319 +vn 0.180761 0.608966 0.772301 +vn 0.118778 0.528459 0.840571 +vn 0.082675 0.500137 0.861965 +vn 0.284463 0.498032 0.819147 +vn 0.422620 0.516312 0.744835 +vn 0.035096 0.318186 0.947356 +vn -0.410077 0.052370 0.910520 +vn 0.049562 0.183081 0.981842 +vn 0.291269 0.239875 0.926054 +vn 0.358043 0.252937 0.898770 +vn 0.362987 0.210150 0.907773 +vn 0.343822 0.221992 0.912381 +vn 0.442213 0.193243 0.875820 +vn 0.550554 0.199133 0.810663 +vn -0.318186 0.933988 0.162420 +vn -0.200354 0.971496 0.126591 +vn -0.223548 0.970855 0.086062 +vn -0.066347 0.990966 0.116398 +vn -0.139744 0.989990 0.019105 +vn -0.061495 0.991150 0.117496 +vn -0.134465 0.990814 0.013977 +vn -0.061037 0.991882 0.111362 +vn -0.123630 0.992309 0.003357 +vn -0.057009 0.994201 0.090915 +vn -0.100101 0.994812 -0.016938 +vn -0.012268 0.998962 0.043367 +vn -0.037477 0.996979 -0.067965 +vn 0.029817 0.999237 -0.024323 +vn 0.018677 0.993408 -0.112918 +vn 0.065371 0.994415 -0.082797 +vn 0.053926 0.989196 -0.136113 +vn 0.033235 0.996521 -0.076144 +vn 0.028626 0.992889 -0.115299 +vn -0.016511 0.998688 -0.048128 +vn -0.013733 0.996429 -0.083041 +vn 0.529496 0.712210 -0.460829 +vn 0.523026 0.708579 -0.473617 +vn 0.789056 -0.011536 -0.614185 +vn -0.302988 0.938353 0.166265 +vn -0.265389 0.938994 0.218726 +vn 0.008972 0.974334 0.224860 +vn 0.017914 0.971709 0.235389 +vn 0.009888 0.967406 0.252937 +vn -0.025452 0.968108 0.249153 +vn -0.026399 0.977172 0.210669 +vn -0.000336 0.993500 0.113742 +vn 0.030061 0.999298 0.021210 +vn 0.023957 0.999512 -0.018830 +vn -0.017853 0.999817 0.001221 +vn 0.536332 0.721305 -0.438215 +vn -0.298685 0.932981 0.200690 +vn -0.114658 -0.798791 -0.590564 +vn -0.370708 -0.526780 -0.764885 +vn -0.291025 -0.616688 -0.731407 +vn -0.143254 -0.625172 -0.767205 +vn 0.027223 -0.640553 -0.767418 +vn 0.111332 -0.662038 -0.741111 +vn 0.135563 -0.740410 -0.658315 +vn 0.095309 -0.862575 -0.496811 +vn 0.031892 -0.950316 -0.309610 +vn -0.117801 -0.895688 -0.428724 +vn 0.703726 0.085910 -0.705222 +vn 0.140812 -0.988342 -0.057833 +vn -0.176916 0.112217 -0.977783 +vn -0.463820 -0.000244 -0.885922 +vn -0.396466 0.495773 -0.772637 +vn -0.251259 0.629933 -0.734855 +vn -0.082247 0.613941 -0.785028 +vn 0.062685 0.432813 -0.899289 +vn 0.114200 0.360729 -0.925626 +vn 0.067263 0.509629 -0.857723 +vn -0.147404 0.666036 -0.731162 +vn -0.333659 0.761101 -0.556200 +vn -0.153478 0.913327 -0.377148 +vn -0.370403 -0.745903 -0.553514 +vn -0.117374 0.990661 0.069308 +vn -0.045228 -0.997101 0.061098 +vn -0.457808 -0.775109 -0.435408 +vn -0.716849 -0.364360 -0.594409 +vn -0.842647 0.257424 -0.472915 +vn -0.742363 -0.635609 -0.211829 +vn -0.978973 -0.126621 -0.159734 +vn -0.920560 0.370373 -0.124027 +vn -0.685263 -0.714591 -0.140477 +vn -0.822535 0.452986 -0.343791 +vn -0.424726 0.841395 -0.334086 +vn 0.104862 -0.938810 0.328043 +vn -0.146855 0.961974 -0.230232 +vn -0.592578 0.640126 -0.488907 +vn -0.615009 0.631336 -0.472365 +vn -0.672750 0.632954 -0.383038 +vn -0.732994 0.635304 -0.243019 +vn -0.733726 0.657796 -0.169988 +vn -0.650349 0.747856 -0.133000 +vn -0.509659 0.849849 -0.133976 +vn -0.374187 0.917051 -0.137638 +vn -0.261879 0.943449 -0.203101 +vn -0.750603 0.280526 0.598193 +vn -0.264351 0.961669 0.072787 +vn -0.292032 0.955870 -0.031159 +vn -0.329691 0.920103 -0.211402 +vn -0.339793 0.915555 -0.215125 +vn -0.354900 0.915159 -0.190985 +vn -0.364147 0.919675 -0.146916 +vn -0.327921 0.934049 -0.141392 +vn -0.236305 0.962859 -0.130497 +vn -0.132908 0.981079 -0.140751 +vn -0.095737 0.987732 -0.123142 +vn -0.095767 0.990448 -0.099002 +vn 0.488937 0.717917 -0.495499 +vn -0.306192 0.943205 0.128666 +vn -0.249184 0.966887 0.054659 +vn -0.214362 0.973754 -0.076144 +vn -0.213904 0.973327 -0.082614 +vn -0.208258 0.974456 -0.083956 +vn -0.190619 0.978057 -0.083712 +vn -0.134281 0.983673 -0.119694 +vn -0.065127 0.987915 -0.140446 +vn -0.005676 0.987548 -0.157170 +vn -0.010773 0.991577 -0.128971 +vn -0.047334 0.995025 -0.087466 +vn 0.511277 0.710990 -0.482742 +vn -0.317698 0.934355 0.161168 +vn 0.115726 -0.988556 0.096622 +vn 0.137333 -0.978088 0.156377 +vn 0.149754 -0.969787 0.192450 +vn 0.113804 -0.975402 0.188665 +vn 0.085177 -0.975860 0.201056 +vn 0.090030 -0.983673 0.155797 +vn 0.118778 -0.991150 0.058962 +vn 0.183233 -0.982940 -0.013886 +vn 0.138249 -0.988586 0.059420 +vn 0.134892 -0.989990 0.041383 +vn 0.729453 -0.456862 -0.509049 +vn -0.047090 -0.987243 0.151891 +vn 0.017121 -0.989441 0.143895 +vn 0.119480 -0.983428 0.136174 +vn 0.126865 -0.983886 0.125919 +vn 0.118809 -0.985748 0.119053 +vn 0.100009 -0.988037 0.117252 +vn 0.092471 -0.989349 0.112217 +vn 0.103183 -0.991180 0.082858 +vn 0.137211 -0.989776 0.038453 +vn 0.148564 -0.988800 0.012665 +vn 0.133518 -0.990966 0.011872 +vn 0.619343 -0.669668 -0.409772 +vn -0.082308 -0.983306 0.162206 +vn -0.013428 -0.993072 0.116581 +vn 0.076205 -0.994476 0.071780 +vn 0.089877 -0.994110 0.060427 +vn 0.092868 -0.994140 0.054903 +vn 0.089145 -0.994568 0.053194 +vn 0.092318 -0.994537 0.048616 +vn 0.110874 -0.993408 0.028413 +vn 0.147404 -0.989044 -0.005463 +vn 0.164617 -0.986053 -0.024293 +vn 0.148503 -0.988769 -0.015656 +vn 0.615223 -0.670400 -0.414716 +vn -0.095187 -0.981842 0.163976 +vn -0.030366 -0.994903 0.096072 +vn 0.037080 -0.999023 0.022736 +vn 0.052431 -0.998535 0.012757 +vn 0.059267 -0.998169 0.011414 +vn 0.060518 -0.998016 0.015534 +vn 0.066469 -0.997681 0.014222 +vn 0.091372 -0.995788 0.001312 +vn 0.133305 -0.990692 -0.027131 +vn 0.154118 -0.987274 -0.039003 +vn 0.143284 -0.989319 -0.026276 +vn 0.612751 -0.670492 -0.418226 +vn -0.094028 -0.983001 0.157659 +vn -0.057375 -0.996307 0.063570 +vn -0.016144 -0.999237 -0.034730 +vn -0.002991 -0.999207 -0.039277 +vn 0.003479 -0.999542 -0.030030 +vn 0.002014 -0.999908 -0.012177 +vn 0.004822 -0.999969 -0.002655 +vn 0.037202 -0.999268 -0.006897 +vn 0.092563 -0.995239 -0.029878 +vn 0.117161 -0.992523 -0.033509 +vn 0.112613 -0.993439 -0.018281 +vn 0.609699 -0.669759 -0.423841 +vn -0.078555 -0.987610 0.135685 +vn -0.137303 -0.990387 -0.015900 +vn -0.142460 -0.977447 -0.155675 +vn -0.135044 -0.979888 -0.146733 +vn -0.135533 -0.984924 -0.107364 +vn -0.147191 -0.987518 -0.055879 +vn -0.153020 -0.987945 -0.022248 +vn -0.097934 -0.995117 -0.011658 +vn -0.015992 -0.999634 -0.021455 +vn 0.047945 -0.998474 -0.026307 +vn 0.066897 -0.997559 -0.018983 +vn 0.600665 -0.671346 -0.434065 +vn -0.027772 -0.995697 0.088137 +vn 0.090670 -0.995666 0.019135 +vn 0.090579 -0.995697 0.019044 +vn 0.188879 -0.970733 0.148198 +vn 0.334513 -0.875454 0.348735 +vn 0.092196 -0.995514 0.021088 +vn 0.092257 -0.995483 0.021180 +vn -0.010163 -0.994171 -0.107089 +vn -0.175970 -0.935575 -0.306040 +vn -0.539750 0.570238 -0.619221 +vn 0.411664 0.682363 0.604022 +vn -0.004852 -0.012940 0.999878 +vn 0.696249 0.084201 0.712821 +vn -0.004883 -0.012940 0.999878 +vn 0.990326 0.131718 0.043519 +vn 0.050478 0.019013 -0.998535 +vn -0.983367 -0.129765 -0.126957 +vn -0.108615 -0.026734 0.993713 +vn 0.991089 0.132176 0.014832 +vn 0.062136 0.020569 -0.997833 +vn -0.673421 -0.080905 -0.734764 +vn -0.673421 -0.080905 -0.734794 +vn -0.689810 -0.083254 -0.719169 +vn -0.689810 -0.083285 -0.719169 +vn -0.981964 -0.129429 -0.137730 +vn -0.979644 -0.153935 -0.128636 +vn 0.215613 0.068148 -0.974090 +vn 0.972839 0.151250 0.175115 +vn 0.549547 0.060335 0.833247 +vn 0.530747 0.056856 0.845607 +vn -0.095462 -0.049409 0.994201 +vn -0.961516 -0.147496 -0.231666 +vn -0.523148 -0.055483 -0.850398 +vn 0.187872 0.063875 -0.980102 +vn 0.970794 0.150517 0.186682 +vn -0.106113 -0.051088 0.993011 +vn -0.567309 0.638081 -0.520554 +vn -0.672536 0.613269 -0.414167 +vn -0.600604 0.726829 -0.333079 +vn -0.865627 0.485733 -0.121220 +vn -0.821436 0.567125 -0.059572 +vn -0.932096 0.276009 0.234443 +vn -0.919126 0.294443 0.261635 +vn -0.874447 0.076205 0.479049 +vn -0.883908 0.053224 0.464583 +vn -0.603076 0.791742 0.096896 +vn -0.825739 -0.022523 0.563585 +vn -0.512680 0.740440 -0.434584 +vn -0.416974 0.906156 -0.070406 +vn -0.663503 0.732475 0.152409 +vn -0.827845 0.397473 0.395764 +vn -0.848079 0.094943 0.521256 +vn -0.826167 -0.037660 0.562120 +vn -0.333415 0.927396 -0.169500 +vn -0.134648 0.947172 0.290994 +vn -0.400525 0.795190 0.455214 +vn -0.672384 0.449507 0.588031 +vn -0.789911 0.101962 0.604633 +vn -0.920225 -0.098300 0.378765 +vn -0.052156 0.977294 0.205359 +vn 0.416059 0.251076 0.873959 +vn 0.175665 0.154698 0.972198 +vn -0.109897 -0.090121 0.989837 +vn -0.221870 -0.384716 0.895932 +vn 0.825556 -0.543962 0.150029 +vn 0.010590 -0.999908 0.005737 +vn 0.058046 -0.998260 -0.008972 +vn 0.495163 0.283273 0.821284 +vn -0.225318 -0.960570 0.162877 +vn -0.348491 -0.769860 0.534623 +vn -0.290658 -0.581988 0.759453 +vn 0.808496 -0.539659 0.234657 +vn -0.564837 -0.555742 -0.609973 +vn -0.793847 -0.511612 -0.328593 +vn -0.928495 -0.333506 0.163152 +vn -0.862911 -0.147649 0.483230 +vn 0.801141 0.079134 -0.593188 +vn -0.487472 -0.583026 -0.649922 +vn -0.719382 -0.151708 -0.677816 +vn -0.918424 -0.140019 -0.369945 +vn -0.987701 -0.086550 0.129978 +vn -0.884121 -0.044404 0.465102 +vn 0.804407 0.067415 -0.590197 +vn -0.601794 -0.283303 -0.746666 +vn -0.756035 0.248848 -0.605335 +vn -0.935301 0.212775 -0.282632 +vn -0.976867 0.133824 0.166753 +vn -0.877590 0.049898 0.476791 +vn 0.810999 0.054262 -0.582507 +vn -0.590533 0.500595 -0.632954 +vn -0.717277 0.486373 -0.498917 +vn -0.895657 0.404645 -0.184393 +vn -0.943510 0.251595 0.215583 +vn -0.863338 0.100070 0.494583 +vn 0.816828 0.033052 -0.575884 +vn -0.622181 0.495407 -0.606128 +vn 0.305063 0.740837 0.598376 +vn 0.108097 0.810297 0.575915 +vn 0.178533 0.713523 0.677450 +vn -0.196326 0.640767 0.742180 +vn -0.140599 0.571123 0.808710 +vn -0.506272 0.343059 0.791162 +vn -0.481643 0.329081 0.812220 +vn -0.671957 0.078188 0.736412 +vn -0.685629 0.098453 0.721244 +vn -0.746391 -0.013153 0.665365 +vn -0.312754 0.825800 0.469253 +vn 0.226600 0.827509 0.513627 +vn -0.115696 0.941649 0.316019 +vn -0.376385 0.766289 0.520676 +vn -0.622211 0.421674 0.659536 +vn -0.721458 0.109836 0.683645 +vn -0.743767 -0.027955 0.667837 +vn -0.001251 0.966521 0.256508 +vn -0.398022 0.916166 -0.046754 +vn -0.608875 0.770653 0.187964 +vn -0.773766 0.437574 0.458022 +vn -0.788141 0.102176 0.606922 +vn -0.584918 -0.058779 0.808954 +vn -0.297617 0.948363 -0.109470 +vn -0.762291 0.112308 -0.637410 +vn -0.908078 0.027039 -0.417859 +vn -0.974181 -0.191931 -0.118686 +vn -0.885250 -0.462874 0.045076 +vn 0.108097 -0.628437 -0.770287 +vn 0.111484 -0.991974 0.059542 +vn 0.085665 -0.991058 0.102023 +vn -0.694693 0.143132 -0.704886 +vn -0.127873 -0.949065 0.287851 +vn -0.533952 -0.791681 0.296762 +vn -0.753136 -0.636464 0.166173 +vn -0.002838 -0.644154 -0.764885 +vn 0.497726 -0.430586 0.752861 +vn 0.165593 -0.398602 0.902036 +vn -0.357982 -0.266305 0.894925 +vn -0.666982 -0.124607 0.734550 +vn 0.763817 0.074709 -0.641072 +vn 0.557848 -0.459914 0.690817 +vn 0.489090 -0.009369 0.872158 +vn 0.141697 -0.015137 0.989776 +vn -0.362529 -0.012940 0.931852 +vn -0.663869 -0.018494 0.747581 +vn 0.762780 0.062502 -0.643605 +vn 0.596301 -0.142186 0.790033 +vn 0.374584 0.382000 0.844813 +vn 0.022004 0.325541 0.945250 +vn -0.414838 0.200018 0.887631 +vn -0.681906 0.072970 0.727775 +vn 0.758141 0.048006 -0.650288 +vn 0.419935 0.619617 0.663076 +vn 0.260475 0.601550 0.755150 +vn -0.079928 0.500717 0.861873 +vn -0.464095 0.308054 0.830470 +vn -0.699973 0.119327 0.704093 +vn 0.755120 0.025727 -0.655049 +vn 0.386670 0.614246 0.687857 +vn -0.797174 -0.603107 -0.026521 +vn -0.244301 -0.755242 -0.608173 +vn 0.250954 -0.962004 -0.107303 +vn -0.112033 -0.515458 0.849513 +vn 0.600024 -0.644642 0.473647 +vn 0.108371 -0.565691 -0.817438 +vn 0.871578 -0.463698 0.159001 +vn 0.153172 -0.253609 -0.955077 +vn 0.984649 -0.136814 0.108341 +vn 0.192694 -0.028748 -0.980804 +vn 0.995270 0.083193 0.049776 +vn 0.059358 0.501267 -0.863247 +vn 0.796503 0.598743 0.084140 +vn -0.371776 0.692984 -0.617664 +vn 0.433210 0.798486 0.417951 +vn -0.580859 0.677633 -0.450972 +vn 0.224006 0.783013 0.580218 +vn -0.743370 0.583178 -0.327494 +vn 0.075320 0.691855 0.718070 +vn -0.787133 0.522355 -0.327952 +vn 0.071963 0.637623 0.766930 +vn -0.818659 0.513230 -0.257607 +vn -0.003662 0.622639 0.782464 +vn -0.911527 0.376415 -0.165441 +vn -0.102359 0.487228 0.867244 +vn -0.980560 0.178625 -0.081088 +vn -0.182897 0.289376 0.939573 +vn -0.523942 -0.729637 0.439375 +vn -0.776849 -0.229896 0.586200 +vn -0.699820 -0.676443 0.229469 +vn -0.117740 -0.979553 0.162999 +vn -0.408979 -0.893521 -0.185186 +vn 0.164525 -0.985473 -0.041627 +vn -0.152776 -0.891842 -0.425672 +vn 0.689962 -0.561754 -0.456435 +vn 0.454360 -0.492691 -0.742149 +vn 0.777703 0.233924 -0.583453 +vn -0.864254 -0.495743 0.085330 +vn -0.681845 -0.595752 -0.424390 +vn -0.450972 -0.565142 -0.690786 +vn 0.231727 -0.250313 -0.940001 +vn -0.972015 -0.229987 0.047243 +vn -0.859523 -0.156987 -0.486312 +vn -0.646535 -0.082919 -0.758324 +vn 0.085025 0.109104 -0.990356 +vn -0.990478 0.048311 0.128666 +vn -0.887539 0.300302 -0.349345 +vn -0.679220 0.417493 -0.603565 +vn 0.060488 0.482131 -0.873989 +vn -0.915403 0.260872 0.306497 +vn -0.761162 0.646199 -0.054811 +vn -0.543809 0.792535 -0.275918 +vn 0.161473 0.760338 -0.629109 +vn -0.769860 0.352367 0.532090 +vn -0.521287 0.792932 0.315378 +vn -0.284402 0.949736 0.130772 +vn 0.353923 0.876370 -0.326548 +vn -0.592029 0.299966 0.747978 +vn -0.230628 0.706900 0.668630 +vn 0.032563 0.856044 0.515854 +vn 0.588946 0.807154 -0.040101 +vn -0.427168 0.113987 0.896939 +vn 0.038881 0.404797 0.913541 +vn 0.329691 0.528428 0.782311 +vn 0.810205 0.564165 0.158971 +vn 0.777703 0.233955 -0.583422 +vn -0.320933 -0.157964 0.933805 +vn 0.213782 -0.038301 0.976104 +vn 0.524552 0.045381 0.850154 +vn 0.956145 0.204260 0.209815 +vn -0.304483 -0.437086 0.846278 +vn 0.242317 -0.495590 0.834040 +vn 0.557573 -0.454939 0.694327 +vn 0.981292 -0.168584 0.092654 +vn -0.379955 -0.644246 0.663717 +vn 0.119327 -0.837092 0.533830 +vn 0.423261 -0.829066 0.365337 +vn 0.881680 -0.446150 -0.153417 +vn -0.488205 -0.725425 0.485153 +vn -0.733879 -0.224799 0.640980 +vn -0.678182 -0.676565 0.286843 +vn -0.099948 -0.977447 0.185858 +vn -0.414655 -0.898648 -0.143132 +vn 0.167852 -0.985076 -0.037324 +vn -0.175298 -0.899380 -0.400403 +vn 0.661214 -0.565142 -0.493301 +vn 0.406384 -0.501968 -0.763451 +vn 0.734886 0.228889 -0.638356 +vn -0.853175 -0.499100 0.151402 +vn -0.705008 -0.606250 -0.367931 +vn -0.492904 -0.578600 -0.649770 +vn 0.169286 -0.264016 -0.949522 +vn -0.965056 -0.234626 0.116550 +vn -0.889401 -0.169530 -0.424482 +vn -0.695791 -0.098636 -0.711417 +vn 0.017121 0.093722 -0.995422 +vn -0.979827 0.044832 0.194708 +vn -0.911100 0.289743 -0.293100 +vn -0.721244 0.404004 -0.562609 +vn -0.001923 0.468429 -0.883480 +vn 0.734916 0.228889 -0.638356 +vn -0.894284 0.260659 0.363689 +vn -0.767388 0.641011 -0.013215 +vn -0.566454 0.784967 -0.250771 +vn 0.113529 0.751061 -0.650380 +vn -0.734428 0.356548 0.577441 +vn -0.504013 0.794977 0.337565 +vn -0.281198 0.950102 0.134922 +vn 0.325205 0.873012 -0.363384 +vn -0.542070 0.308603 0.781579 +vn -0.189550 0.716208 0.671590 +vn 0.061708 0.864345 0.499069 +vn 0.579547 0.809656 -0.092471 +vn 0.734886 0.228858 -0.638356 +vn -0.366283 0.125919 0.921934 +vn 0.097873 0.419568 0.902402 +vn 0.378338 0.542711 0.749840 +vn 0.815332 0.571123 0.094913 +vn -0.255989 -0.144780 0.955748 +vn 0.279550 -0.021516 0.959868 +vn 0.580523 0.061892 0.811853 +vn 0.966765 0.212897 0.141392 +vn -0.243629 -0.425153 0.871670 +vn 0.301462 -0.480850 0.823328 +vn 0.606250 -0.440687 0.661977 +vn 0.986419 -0.161626 0.028596 +vn -0.329936 -0.635670 0.697867 +vn 0.160710 -0.827784 0.537492 +vn 0.452467 -0.820734 0.348704 +vn 0.872219 -0.443617 -0.205817 +vn -0.279611 -0.239448 0.929746 +vn -0.728965 -0.331675 0.598804 +vn -0.241798 -0.437696 0.865963 +vn 0.251595 -0.073305 0.965026 +vn 0.314890 -0.405164 0.858272 +vn 0.554064 0.045930 0.831172 +vn 0.623554 -0.318369 0.713981 +vn 0.945769 0.279214 0.165960 +vn 0.996704 0.012146 0.080050 +vn 0.728965 0.331675 -0.598804 +vn 0.728965 0.331675 -0.598773 +vn -0.260384 -0.611774 0.746940 +vn 0.283761 -0.696493 0.659017 +vn 0.589404 -0.638203 0.495224 +vn 0.971679 -0.222236 -0.080233 +vn -0.332560 -0.735099 0.590747 +vn 0.162969 -0.902921 0.397626 +vn 0.456801 -0.864834 0.208258 +vn 0.874447 -0.388348 -0.290597 +vn -0.447310 -0.788965 0.421186 +vn -0.029084 -0.993042 0.113865 +vn 0.245918 -0.963744 -0.103214 +vn 0.719901 -0.460891 -0.518937 +vn -0.587207 -0.765099 0.264107 +vn -0.263222 -0.953124 -0.149022 +vn -0.011078 -0.919919 -0.391858 +vn 0.531480 -0.428755 -0.730522 +vn 0.728965 0.331706 -0.598804 +vn -0.730918 -0.667196 0.143406 +vn -0.503769 -0.789239 -0.351085 +vn -0.275155 -0.740013 -0.613666 +vn 0.337901 -0.296884 -0.893094 +vn -0.856594 -0.510117 0.077425 +vn -0.714072 -0.526353 -0.461501 +vn -0.506058 -0.451430 -0.734886 +vn 0.168676 -0.085330 -0.981964 +vn -0.945067 -0.317820 0.076235 +vn -0.862178 -0.204505 -0.463485 +vn -0.668630 -0.098056 -0.737083 +vn 0.049501 0.173650 -0.983551 +vn -0.982879 -0.119541 0.140019 +vn -0.925474 0.127354 -0.356700 +vn -0.738121 0.266243 -0.619861 +vn -0.001404 0.440687 -0.897641 +vn -0.964293 0.054506 0.259072 +vn -0.894345 0.418653 -0.157476 +vn -0.703970 0.586077 -0.401135 +vn 0.023591 0.675130 -0.737297 +vn 0.728935 0.331706 -0.598804 +vn -0.892117 0.177862 0.415265 +vn -0.773553 0.625111 0.103916 +vn -0.571368 0.812708 -0.114170 +vn 0.120792 0.841243 -0.526933 +vn -0.777367 0.231697 0.584796 +vn -0.581469 0.715232 0.387677 +vn -0.360485 0.911618 0.197333 +vn 0.275369 0.913755 -0.298593 +vn -0.637471 0.207831 0.741874 +vn -0.347331 0.675314 0.650594 +vn -0.103458 0.867794 0.485977 +vn 0.463790 0.881649 -0.087039 +vn -0.493759 0.109928 0.862606 +vn -0.106815 0.511429 0.852626 +vn 0.160588 0.687887 0.707785 +vn 0.657338 0.749779 0.075533 +vn -0.368084 -0.047121 0.928587 +vn 0.103488 0.248543 0.963042 +vn 0.391491 0.399304 0.829005 +vn 0.826594 0.538224 0.164373 +vn 0.869930 0.207892 -0.447157 +vn 0.764183 0.197546 -0.613941 +vn 0.834101 0.302194 -0.461409 +vn 0.965789 0.207770 -0.155034 +vn 0.877438 0.440321 -0.190191 +vn 0.906308 0.209906 -0.366710 +vn 0.854885 0.345256 -0.387158 +vn 0.889004 0.209113 -0.407300 +vn 0.845332 0.324046 -0.424696 +vn 0.972869 0.189856 0.131993 +vn 0.842891 0.532029 0.080264 +vn 0.805780 0.125584 0.578692 +vn 0.631306 0.584796 0.509323 +vn 0.701254 0.095614 0.706442 +vn 0.520554 0.571215 0.634571 +vn 0.775536 0.367412 -0.513291 +vn 0.733024 0.601154 -0.318217 +vn 0.770806 0.438887 -0.461684 +vn 0.773949 0.403546 -0.487960 +vn 0.630360 0.768731 -0.108066 +vn 0.346110 0.902432 0.256508 +vn 0.225166 0.900174 0.372753 +vn 0.709952 0.386090 -0.588977 +vn 0.571215 0.647175 -0.504776 +vn 0.676626 0.465712 -0.570299 +vn 0.693960 0.426313 -0.580187 +vn 0.392254 0.836482 -0.382641 +vn 0.026582 0.993347 -0.111942 +vn -0.105747 0.994324 -0.008850 +vn 0.654866 0.353191 -0.668111 +vn 0.435377 0.566088 -0.699942 +vn 0.597552 0.418500 -0.683920 +vn 0.626820 0.386212 -0.676656 +vn 0.192389 0.717124 -0.669820 +vn -0.241646 0.833186 -0.497330 +vn -0.383557 0.828486 -0.408002 +vn 0.625080 0.277566 -0.729514 +vn 0.361919 0.379589 -0.851405 +vn 0.554796 0.309915 -0.772057 +vn 0.590503 0.294046 -0.751518 +vn 0.084262 0.442701 -0.892666 +vn -0.386700 0.464888 -0.796411 +vn -0.533799 0.447035 -0.717765 +vn 0.628559 0.179479 -0.756737 +vn 0.370525 0.137669 -0.918546 +vn 0.559801 0.169073 -0.811151 +vn 0.594775 0.174444 -0.784722 +vn 0.096927 0.086673 -0.991485 +vn -0.369732 -0.012848 -0.929014 +vn -0.516221 -0.047761 -0.855098 +vn 0.664388 0.085177 -0.742485 +vn 0.458876 -0.094852 -0.883389 +vn 0.611225 0.033723 -0.790704 +vn 0.638417 0.059511 -0.767357 +vn 0.226936 -0.255470 -0.939787 +vn -0.195257 -0.472060 -0.859645 +vn -0.335521 -0.523392 -0.783227 +vn 0.722953 0.019959 -0.690573 +vn 0.603290 -0.255684 -0.755394 +vn 0.695303 -0.059877 -0.716178 +vn 0.709830 -0.019990 -0.704062 +vn 0.439467 -0.492141 -0.751396 +vn 0.089908 -0.789697 -0.606830 +vn -0.040132 -0.852351 -0.521409 +vn 0.788568 0.001282 -0.614917 +vn 0.765099 -0.301706 -0.568804 +vn 0.789483 -0.086673 -0.607562 +vn 0.789788 -0.042726 -0.611835 +vn 0.677572 -0.559893 -0.476852 +vn 0.409467 -0.880612 -0.238350 +vn 0.290780 -0.946501 -0.139775 +vn 0.843623 0.034150 -0.535783 +vn 0.900937 -0.220618 -0.373638 +vn 0.868557 -0.039460 -0.493973 +vn 0.856929 -0.002655 -0.515366 +vn 0.877438 -0.440565 -0.189642 +vn 0.677694 -0.720481 0.147008 +vn 0.568590 -0.780633 0.259346 +vn 0.873409 0.109806 -0.474380 +vn 0.974395 -0.034120 -0.222205 +vn 0.911313 0.069063 -0.405805 +vn 0.893246 0.089511 -0.440504 +vn 0.985534 -0.166112 0.033174 +vn 0.822748 -0.352153 0.446089 +vn 0.718833 -0.399182 0.569109 +vn 0.862636 0.241218 -0.444533 +vn 0.756767 0.231544 -0.611286 +vn 0.824213 0.334788 -0.456679 +vn 0.959288 0.237495 -0.152715 +vn 0.864498 0.468215 -0.182653 +vn 0.899197 0.242531 -0.364147 +vn 0.844020 0.376812 -0.381573 +vn 0.881771 0.242103 -0.404706 +vn 0.834925 0.356151 -0.419507 +vn 0.967681 0.213660 0.133854 +vn 0.828181 0.553178 0.089785 +vn 0.803674 0.135289 0.579455 +vn 0.616474 0.590899 0.520310 +vn 0.700339 0.099704 0.706778 +vn 0.506485 0.571612 0.645497 +vn 0.763726 0.399457 -0.507035 +vn 0.715354 0.627735 -0.306864 +vn 0.757195 0.469680 -0.453902 +vn 0.761193 0.435011 -0.480911 +vn 0.608722 0.787896 -0.092959 +vn 0.321940 0.905911 0.275033 +vn 0.201453 0.897855 0.391461 +vn 0.697409 0.417951 -0.582141 +vn 0.551836 0.673269 -0.492080 +vn 0.662008 0.496200 -0.561693 +vn 0.680380 0.457503 -0.572466 +vn 0.368084 0.854915 -0.365490 +vn -0.000977 0.995849 -0.090670 +vn -0.133000 0.991028 0.012665 +vn 0.643055 0.385266 -0.661824 +vn 0.417737 0.592669 -0.688620 +vn 0.583941 0.449263 -0.676107 +vn 0.614093 0.417676 -0.669637 +vn 0.170751 0.736290 -0.654714 +vn -0.265786 0.836665 -0.478835 +vn -0.407270 0.826136 -0.389294 +vn 0.615162 0.310160 -0.724784 +vn 0.348979 0.407514 -0.843867 +vn 0.543901 0.341472 -0.766472 +vn 0.580096 0.326151 -0.746361 +vn 0.069582 0.463820 -0.883175 +vn -0.401532 0.470992 -0.785394 +vn -0.547868 0.447432 -0.706809 +vn 0.621265 0.212806 -0.754112 +vn 0.364025 0.167394 -0.916196 +vn 0.552660 0.201697 -0.808588 +vn 0.587542 0.207465 -0.782128 +vn 0.091739 0.110477 -0.989624 +vn -0.371838 -0.003143 -0.928251 +vn -0.517106 -0.043641 -0.854762 +vn 0.659688 0.119236 -0.741966 +vn 0.458815 -0.063295 -0.886258 +vn 0.607837 0.067385 -0.791162 +vn 0.634388 0.093417 -0.767327 +vn 0.231208 -0.229011 -0.945555 +vn -0.184667 -0.458785 -0.869106 +vn -0.323252 -0.515549 -0.793512 +vn 0.720176 0.054567 -0.691610 +vn 0.607959 -0.222816 -0.762047 +vn 0.694662 -0.025422 -0.718863 +vn 0.708121 0.014557 -0.705924 +vn 0.450667 -0.463729 -0.762749 +vn 0.109836 -0.773766 -0.623829 +vn -0.018220 -0.841792 -0.539476 +vn 0.786493 0.036073 -0.616504 +vn 0.771477 -0.268349 -0.576830 +vn 0.789850 -0.051943 -0.611042 +vn 0.788934 -0.007935 -0.614368 +vn 0.691305 -0.530747 -0.490249 +vn 0.432783 -0.863735 -0.258095 +vn 0.316233 -0.934965 -0.160680 +vn 0.840877 0.068789 -0.536790 +vn 0.905576 -0.187750 -0.380291 +vn 0.867916 -0.005036 -0.496628 +vn 0.855251 0.031892 -0.517228 +vn 0.888638 -0.412152 -0.200995 +vn 0.697592 -0.704550 0.130039 +vn 0.590503 -0.770074 0.241279 +vn 0.868740 0.143864 -0.473861 +vn 0.974334 -0.002594 -0.225043 +vn 0.907956 0.102756 -0.406262 +vn 0.889218 0.123417 -0.440474 +vn 0.989807 -0.139653 0.027406 +vn 0.833369 -0.338878 0.436598 +vn 0.731101 -0.391369 0.558794 +vn 0.862636 0.241218 -0.444502 +vn 0.899197 0.242500 -0.364147 +vn 0.506485 0.571612 0.645527 +vn 0.763726 0.399487 -0.507035 +vn 0.757195 0.469680 -0.453871 +vn 0.697439 0.417951 -0.582141 +vn -0.133000 0.991028 0.012696 +vn 0.583911 0.449263 -0.676107 +vn 0.348979 0.407483 -0.843867 +vn 0.543931 0.341472 -0.766472 +vn -0.401532 0.470992 -0.785424 +vn 0.091708 0.110477 -0.989624 +vn 0.659719 0.119236 -0.741966 +vn 0.694662 -0.025452 -0.718863 +vn 0.109836 -0.773797 -0.623829 +vn 0.786493 0.036103 -0.616504 +vn 0.771477 -0.268349 -0.576861 +vn 0.840877 0.068789 -0.536821 +vn 0.855220 0.031892 -0.517228 +vn 0.907956 0.102725 -0.406262 +vn 0.731101 -0.391369 0.558824 +vn 0.841060 0.204260 -0.500870 +vn 0.784539 0.059572 -0.617145 +vn 0.798791 0.241432 -0.550981 +vn 0.865413 0.414045 -0.282052 +vn 0.760613 0.506211 -0.406415 +vn 0.723441 0.675710 0.141453 +vn 0.536637 0.839961 -0.080142 +vn 0.548235 0.746971 0.376080 +vn 0.335978 0.933622 0.124271 +vn 0.752129 0.250740 -0.609424 +vn 0.644826 0.529283 -0.551347 +vn 0.330332 0.881069 -0.338450 +vn 0.101566 0.980316 -0.169195 +vn 0.708152 0.230750 -0.667257 +vn 0.535752 0.479720 -0.694845 +vn 0.135899 0.792749 -0.594165 +vn -0.119327 0.879971 -0.459731 +vn 0.673544 0.184545 -0.715720 +vn 0.449904 0.365093 -0.814997 +vn -0.017029 0.588488 -0.808313 +vn -0.293130 0.647877 -0.703055 +vn 0.653584 0.119144 -0.747368 +vn 0.400433 0.202857 -0.893582 +vn -0.105197 0.299326 -0.948302 +vn -0.393292 0.319315 -0.862148 +vn 0.651326 0.044496 -0.757439 +vn 0.394848 0.017670 -0.918546 +vn -0.115177 -0.030641 -0.992859 +vn -0.404645 -0.055605 -0.912748 +vn 0.667104 -0.028016 -0.744407 +vn 0.433973 -0.162206 -0.886196 +vn -0.045442 -0.351268 -0.935148 +vn -0.325419 -0.419904 -0.847194 +vn 0.698508 -0.087374 -0.710196 +vn 0.511856 -0.309458 -0.801355 +vn 0.093356 -0.613666 -0.783990 +vn -0.167669 -0.718070 -0.675436 +vn 0.740776 -0.124516 -0.660085 +vn 0.616688 -0.401624 -0.677023 +vn 0.280160 -0.777917 -0.562395 +vn 0.044557 -0.904721 -0.423658 +vn 0.787439 -0.133824 -0.601642 +vn 0.732444 -0.424696 -0.532060 +vn 0.486465 -0.819056 -0.304086 +vn 0.278970 -0.951415 -0.130131 +vn 0.831416 -0.113834 -0.543809 +vn 0.841578 -0.375134 -0.388592 +vn 0.680929 -0.730735 -0.048372 +vn 0.499893 -0.851070 0.160375 +vn 0.866024 -0.067629 -0.495376 +vn 0.927396 -0.260506 -0.268410 +vn 0.833857 -0.526444 0.165746 +vn 0.673696 -0.618976 0.403699 +vn 0.885983 -0.002228 -0.463698 +vn 0.976867 -0.098239 -0.189856 +vn 0.922025 -0.237312 0.305765 +vn 0.773888 -0.290414 0.562761 +vn 0.888241 0.072390 -0.453627 +vn 0.982452 0.086886 -0.164861 +vn 0.932005 0.092654 0.350291 +vn 0.785211 0.084506 0.613392 +vn 0.872463 0.144902 -0.466659 +vn 0.943327 0.266793 -0.197241 +vn 0.862270 0.413282 0.292611 +vn 0.705985 0.448805 0.547838 +vn 0.841060 0.204291 -0.500870 +vn 0.784570 0.059572 -0.617145 +vn 0.760613 0.506211 -0.406384 +vn 0.723441 0.675680 0.141423 +vn 0.336009 0.933622 0.124271 +vn 0.535722 0.479720 -0.694845 +vn 0.135899 0.792749 -0.594134 +vn -0.016999 0.588488 -0.808313 +vn -0.293100 0.647877 -0.703055 +vn 0.400433 0.202857 -0.893551 +vn 0.651326 0.044496 -0.757469 +vn 0.394818 0.017670 -0.918577 +vn -0.045442 -0.351238 -0.935148 +vn 0.093387 -0.613666 -0.783990 +vn 0.280160 -0.777947 -0.562395 +vn 0.486496 -0.819056 -0.304056 +vn 0.279000 -0.951415 -0.130131 +vn 0.841578 -0.375134 -0.388562 +vn 0.499924 -0.851070 0.160375 +vn 0.866024 -0.067629 -0.495346 +vn 0.976867 -0.098270 -0.189856 +vn 0.922056 -0.237281 0.305765 +vn 0.982452 0.086886 -0.164830 +vn 0.932035 0.092685 0.350291 +vn 0.785241 0.084506 0.613392 +vn 0.872463 0.144932 -0.466659 +vn 0.943327 0.266793 -0.197211 +vn 0.862300 0.413282 0.292611 +vn 0.705985 0.448805 0.547807 +vn -0.602557 -0.161321 -0.781579 +vn 0.115879 -0.991913 0.051546 +vn 0.569872 -0.461409 0.679922 +vn 0.613330 -0.220466 0.758415 +vn 0.618824 -0.013581 0.785363 +vn 0.597156 0.195227 0.777978 +vn 0.547441 0.400739 0.734611 +vn 0.291025 0.843501 0.451399 +vn -0.326334 0.885983 -0.329356 +vn 0.618976 -0.017457 0.785180 +vn 0.066836 -0.997681 -0.011322 +vn -0.473830 -0.584338 -0.658773 +vn -0.560137 -0.358684 -0.746696 +vn -0.603107 -0.157506 -0.781915 +vn -0.619861 0.051881 -0.782983 +vn -0.608539 0.264595 -0.748070 +vn -0.438063 0.757622 -0.483779 +vn 0.158361 0.943083 0.292367 +vn -0.298898 -0.455184 0.838710 +vn -0.785272 -0.055574 0.616627 +vn -0.454390 -0.633290 0.626423 +vn 0.329417 -0.651906 0.682974 +vn 0.086978 -0.932493 0.350475 +vn 0.897397 -0.441145 0.003815 +vn 0.716025 -0.653005 -0.246620 +vn -0.653432 -0.658773 0.372845 +vn -0.224281 -0.973388 -0.046602 +vn 0.481399 -0.685232 -0.546495 +vn -0.843684 -0.517258 0.143559 +vn -0.521531 -0.751701 -0.403577 +vn 0.255623 -0.517869 -0.816340 +vn -0.969604 -0.244484 0.005127 +vn -0.717399 -0.325297 -0.616016 +vn 0.106540 -0.194739 -0.975036 +vn -0.996765 0.080172 -0.004120 +vn -0.758629 0.180090 -0.626087 +vn 0.074801 0.186163 -0.979644 +vn -0.922208 0.369335 0.114292 +vn -0.642232 0.628254 -0.439070 +vn 0.161504 0.522233 -0.837336 +vn -0.766472 0.551256 0.329508 +vn -0.400708 0.910184 -0.104678 +vn 0.342265 0.734306 -0.586200 +vn -0.566881 0.576373 0.588549 +vn -0.091281 0.950774 0.296030 +vn 0.575640 0.766411 -0.284951 +vn 0.784570 0.059542 -0.617145 +vn -0.376293 0.430647 0.820307 +vn 0.205054 0.727439 0.654775 +vn 0.800806 0.598712 -0.014435 +vn -0.250587 0.154088 0.955718 +vn 0.401807 0.299661 0.865291 +vn 0.950530 0.275399 0.143498 +vn -0.224036 -0.170202 0.959593 +vn 0.444899 -0.205390 0.871700 +vn 0.983520 -0.105411 0.146764 +vn -0.298898 -0.455153 0.838710 +vn -0.454360 -0.633290 0.626453 +vn 0.897427 -0.441115 0.003815 +vn 0.716056 -0.653005 -0.246651 +vn 0.478042 0.346324 -0.807154 +vn 0.590289 0.472304 -0.654561 +vn 0.786279 0.049104 -0.615894 +vn -0.653401 -0.658773 0.372845 +vn 0.732505 0.487350 -0.475265 +vn 0.866543 0.386975 -0.315104 +vn 0.106510 -0.194739 -0.975036 +vn 0.956420 0.196387 -0.216041 +vn -0.996765 0.080203 -0.004120 +vn -0.758660 0.180090 -0.626057 +vn 0.074770 0.186193 -0.979644 +vn 0.977935 -0.034547 -0.205969 +vn -0.922208 0.369366 0.114322 +vn 0.161473 0.522233 -0.837367 +vn 0.925565 -0.243690 -0.289712 +vn -0.766472 0.551256 0.329539 +vn 0.342235 0.734306 -0.586200 +vn 0.813379 -0.373730 -0.445723 +vn -0.566851 0.576373 0.588549 +vn 0.671163 -0.388348 -0.631397 +vn -0.376263 0.430647 0.820307 +vn 0.205023 0.727439 0.654775 +vn 0.800806 0.598743 -0.014435 +vn 0.537034 -0.283303 -0.794519 +vn 0.950530 0.275369 0.143498 +vn 0.447127 -0.088626 -0.890042 +vn 0.444899 -0.205390 0.871670 +vn 0.983520 -0.105411 0.146733 +vn 0.425611 0.141789 -0.893704 +s 1 +f 1204/1/1 1236/2/2 1176/3/3 +f 1132/4/4 1176/3/3 1177/5/5 +f 1131/6/6 1177/5/5 980/7/7 +f 980/7/7 448/8/8 466/9/9 +f 940/10/10 980/7/7 466/9/9 +f 466/9/9 448/8/8 175/11/11 +f 223/12/12 175/11/11 174/13/13 +f 224/14/14 174/13/13 117/15/15 +f 1204/1/1 1176/3/3 1132/4/4 +f 1132/4/4 1177/5/5 1131/6/6 +f 1131/6/6 980/7/7 940/10/10 +f 466/9/9 175/11/11 223/12/12 +f 223/12/12 174/13/13 224/14/14 +f 224/14/14 117/15/15 148/16/16 +f 1260/17/17 1208/18/18 1238/19/19 +f 1253/20/20 1109/21/21 1208/18/18 +f 1183/22/22 979/23/23 1109/21/21 +f 1013/24/24 868/25/25 979/23/23 +f 889/26/26 760/27/27 868/25/25 +f 760/27/27 580/28/28 593/29/29 +f 773/30/30 580/28/28 760/27/27 +f 580/28/28 487/31/31 593/29/29 +f 464/32/32 378/33/33 487/31/31 +f 341/34/34 247/35/35 378/33/33 +f 170/36/36 144/37/37 247/35/35 +f 102/38/38 119/39/39 144/37/37 +f 119/39/39 1260/17/17 1238/19/19 +f 95/40/40 1260/17/17 119/39/39 +f 1260/17/17 1253/20/20 1208/18/18 +f 1253/20/20 1183/22/22 1109/21/21 +f 1183/22/22 1013/24/24 979/23/23 +f 1013/24/24 889/26/26 868/25/25 +f 889/26/26 773/30/30 760/27/27 +f 580/28/28 464/32/32 487/31/31 +f 464/32/32 341/34/34 378/33/33 +f 341/34/34 170/36/36 247/35/35 +f 170/36/36 102/38/38 144/37/37 +f 102/38/38 95/40/40 119/39/39 +f 1237/41/41 1207/42/42 661/43/43 +f 1237/41/41 1253/20/20 1260/17/17 +f 697/44/44 696/45/45 1253/20/20 +f 1260/17/17 697/44/44 1253/20/20 +f 1207/42/42 1108/46/46 661/43/43 +f 1207/42/42 1183/22/22 1253/20/20 +f 696/45/45 695/47/47 1183/22/22 +f 1253/20/20 696/45/45 1183/22/22 +f 1108/46/46 978/48/48 661/43/43 +f 1108/46/46 1013/24/24 1183/22/22 +f 695/47/47 694/49/49 1013/24/24 +f 1183/22/22 695/47/47 1013/24/24 +f 978/48/48 867/50/50 661/43/43 +f 978/48/48 889/26/26 1013/24/24 +f 694/49/49 693/51/51 889/26/26 +f 1013/24/24 694/49/49 889/26/26 +f 867/50/50 759/52/52 661/43/43 +f 867/50/50 773/30/30 889/26/26 +f 693/51/51 690/53/53 773/30/30 +f 889/26/26 693/51/51 773/30/30 +f 759/52/52 592/54/54 661/43/43 +f 773/30/30 592/54/54 580/28/28 +f 759/52/52 592/54/54 773/30/30 +f 690/53/53 668/55/55 580/28/28 +f 773/30/30 580/28/28 690/53/53 +f 592/54/54 486/56/56 661/43/43 +f 592/54/54 464/32/32 580/28/28 +f 580/28/28 464/32/32 668/55/55 +f 486/56/56 377/57/57 661/43/43 +f 486/56/56 341/34/34 464/32/32 +f 464/32/32 668/55/55 341/34/34 +f 377/57/57 246/58/58 661/43/43 +f 377/57/57 170/36/36 341/34/34 +f 341/34/34 668/55/55 170/36/36 +f 246/58/58 143/59/59 661/43/43 +f 246/58/58 102/38/38 170/36/36 +f 170/36/36 668/55/55 102/38/38 +f 143/59/59 118/60/60 661/43/43 +f 143/59/59 95/40/40 102/38/38 +f 102/38/38 668/55/55 95/40/40 +f 118/60/60 117/15/15 661/43/43 +f 118/60/60 95/40/40 94/61/61 +f 95/40/40 668/55/55 94/61/61 +f 117/15/15 142/62/62 661/43/43 +f 117/15/15 94/61/61 101/63/63 +f 94/61/61 668/55/55 101/63/63 +f 142/62/62 245/64/64 661/43/43 +f 142/62/62 101/63/63 169/65/65 +f 101/63/63 668/55/55 169/65/65 +f 245/64/64 376/66/66 661/43/43 +f 245/64/64 169/65/65 340/67/67 +f 169/65/65 668/55/55 340/67/67 +f 376/66/66 485/68/68 661/43/43 +f 376/66/66 340/67/67 463/69/69 +f 340/67/67 668/55/55 463/69/69 +f 485/68/68 591/70/70 661/43/43 +f 485/68/68 463/69/69 579/71/71 +f 463/69/69 668/55/55 579/71/71 +f 591/70/70 647/72/72 661/43/43 +f 591/70/70 579/71/71 646/73/73 +f 579/71/71 668/55/55 646/73/73 +f 647/72/72 758/74/74 661/43/43 +f 647/72/72 646/73/73 772/75/75 +f 668/55/55 690/53/53 772/75/75 +f 646/73/73 668/55/55 772/75/75 +f 758/74/74 866/76/76 661/43/43 +f 758/74/74 772/75/75 888/77/77 +f 690/53/53 693/51/51 888/77/77 +f 772/75/75 690/53/53 888/77/77 +f 866/76/76 977/78/78 661/43/43 +f 866/76/76 888/77/77 1012/79/79 +f 693/51/51 694/49/49 1012/79/79 +f 888/77/77 693/51/51 1012/79/79 +f 977/78/78 1107/80/80 661/43/43 +f 977/78/78 1012/79/79 1182/81/81 +f 694/49/49 695/47/47 1182/81/81 +f 1012/79/79 694/49/49 1182/81/81 +f 1107/80/80 1206/82/82 661/43/43 +f 1107/80/80 1182/81/81 1252/83/83 +f 695/47/47 696/45/45 1252/83/83 +f 1182/81/81 695/47/47 1252/83/83 +f 1206/82/82 1236/2/2 661/43/43 +f 1206/82/82 1252/83/83 1259/84/84 +f 696/45/45 697/44/44 1259/84/84 +f 1252/83/83 696/45/45 1259/84/84 +f 1236/2/2 1237/41/41 661/43/43 +f 1236/2/2 1259/84/84 1260/17/17 +f 1259/84/84 697/44/44 1260/17/17 +f 1237/41/41 1253/20/20 1207/42/42 +f 1207/42/42 1183/22/22 1108/46/46 +f 1108/46/46 1013/24/24 978/48/48 +f 978/48/48 889/26/26 867/50/50 +f 867/50/50 773/30/30 759/52/52 +f 592/54/54 486/56/56 464/32/32 +f 486/56/56 377/57/57 341/34/34 +f 377/57/57 246/58/58 170/36/36 +f 246/58/58 143/59/59 102/38/38 +f 143/59/59 118/60/60 95/40/40 +f 118/60/60 117/15/15 94/61/61 +f 117/15/15 101/63/63 142/62/62 +f 142/62/62 169/65/65 245/64/64 +f 245/64/64 340/67/67 376/66/66 +f 376/66/66 463/69/69 485/68/68 +f 485/68/68 579/71/71 591/70/70 +f 591/70/70 646/73/73 647/72/72 +f 647/72/72 772/75/75 758/74/74 +f 758/74/74 888/77/77 866/76/76 +f 866/76/76 1012/79/79 977/78/78 +f 977/78/78 1182/81/81 1107/80/80 +f 1107/80/80 1252/83/83 1206/82/82 +f 1206/82/82 1259/84/84 1236/2/2 +f 1236/2/2 1260/17/17 1237/41/41 +f 777/85/85 659/86/86 766/87/87 +f 777/85/85 824/88/88 835/89/89 +f 835/89/89 924/90/90 945/91/91 +f 945/91/91 1030/92/92 1068/93/93 +f 1068/93/93 1067/94/94 1129/95/95 +f 1129/95/95 1117/96/96 1163/97/97 +f 1163/97/97 1155/98/98 1200/99/99 +f 1200/99/99 1160/100/100 1203/101/101 +f 1203/101/101 1161/102/102 1205/103/103 +f 1205/103/103 660/104/104 1161/102/102 +f 766/87/87 659/86/86 734/105/105 +f 766/87/87 789/106/106 824/88/88 +f 824/88/88 846/107/107 924/90/90 +f 924/90/90 895/108/108 1030/92/92 +f 1030/92/92 906/109/109 1067/94/94 +f 1067/94/94 917/110/110 1117/96/96 +f 1117/96/96 931/111/111 1155/98/98 +f 1155/98/98 937/112/112 1160/100/100 +f 1160/100/100 1161/102/102 938/113/113 +f 1161/102/102 660/104/104 938/113/113 +f 734/105/105 659/86/86 701/114/114 +f 734/105/105 708/115/115 789/106/106 +f 789/106/106 731/116/116 846/107/107 +f 846/107/107 738/117/117 895/108/108 +f 895/108/108 743/118/118 906/109/109 +f 906/109/109 746/119/119 917/110/110 +f 917/110/110 752/120/120 931/111/111 +f 931/111/111 754/121/121 937/112/112 +f 937/112/112 938/113/113 756/122/122 +f 938/113/113 660/104/104 756/122/122 +f 701/114/114 659/86/86 645/123/123 +f 708/115/115 645/123/123 641/124/124 +f 701/114/114 645/123/123 708/115/115 +f 731/116/116 641/124/124 632/125/125 +f 708/115/115 641/124/124 731/116/116 +f 738/117/117 632/125/125 611/126/126 +f 731/116/116 632/125/125 738/117/117 +f 743/118/118 611/126/126 606/127/127 +f 738/117/117 611/126/126 743/118/118 +f 746/119/119 606/127/127 604/128/128 +f 743/118/118 606/127/127 746/119/119 +f 752/120/120 604/128/128 599/129/129 +f 746/119/119 604/128/128 752/120/120 +f 754/121/121 599/129/129 596/130/130 +f 752/120/120 599/129/129 754/121/121 +f 756/122/122 597/131/131 596/130/130 +f 754/121/121 756/122/122 596/130/130 +f 756/122/122 660/104/104 597/131/131 +f 645/123/123 659/86/86 630/132/132 +f 645/123/123 566/133/133 641/124/124 +f 641/124/124 507/134/134 632/125/125 +f 632/125/125 459/135/135 611/126/126 +f 611/126/126 445/136/136 606/127/127 +f 606/127/127 435/137/137 604/128/128 +f 604/128/128 420/138/138 599/129/129 +f 599/129/129 596/130/130 414/139/139 +f 596/130/130 597/131/131 415/140/140 +f 597/131/131 660/104/104 415/140/140 +f 630/132/132 659/86/86 587/141/141 +f 630/132/132 529/142/142 566/133/133 +f 566/133/133 427/143/143 507/134/134 +f 507/134/134 320/144/144 459/135/135 +f 459/135/135 288/145/145 445/136/136 +f 445/136/136 235/146/146 435/137/137 +f 435/137/137 197/147/147 420/138/138 +f 420/138/138 414/139/139 190/148/148 +f 414/139/139 415/140/140 191/149/149 +f 415/140/140 660/104/104 191/149/149 +f 587/141/141 659/86/86 578/150/150 +f 587/141/141 525/151/151 529/142/142 +f 529/142/142 408/152/152 427/143/143 +f 427/143/143 286/153/153 320/144/144 +f 320/144/144 221/154/154 288/145/145 +f 288/145/145 187/155/155 235/146/146 +f 235/146/146 154/156/156 197/147/147 +f 197/147/147 146/157/157 190/148/148 +f 190/148/148 147/158/158 191/149/149 +f 191/149/149 660/104/104 147/158/158 +f 578/150/150 659/86/86 577/159/159 +f 578/150/150 524/160/160 525/151/151 +f 525/151/151 409/161/161 408/152/152 +f 408/152/152 287/162/162 286/153/153 +f 286/153/153 222/163/163 221/154/154 +f 221/154/154 188/164/164 187/155/155 +f 187/155/155 153/165/165 154/156/156 +f 154/156/156 145/166/166 146/157/157 +f 147/158/158 145/166/166 148/16/16 +f 146/157/157 145/166/166 147/158/158 +f 147/158/158 148/16/16 660/104/104 +f 577/159/159 659/86/86 586/167/167 +f 577/159/159 530/168/168 524/160/160 +f 524/160/160 428/169/169 409/161/161 +f 409/161/161 321/170/170 287/162/162 +f 287/162/162 289/171/171 222/163/163 +f 222/163/163 236/172/172 188/164/164 +f 188/164/164 196/173/173 153/165/165 +f 153/165/165 189/174/174 145/166/166 +f 145/166/166 192/175/175 148/16/16 +f 148/16/16 192/175/175 660/104/104 +f 586/167/167 659/86/86 629/176/176 +f 586/167/167 567/177/177 530/168/168 +f 530/168/168 508/178/178 428/169/169 +f 428/169/169 460/179/179 321/170/170 +f 321/170/170 446/180/180 289/171/171 +f 289/171/171 436/181/181 236/172/172 +f 236/172/172 419/182/182 196/173/173 +f 196/173/173 413/183/183 189/174/174 +f 189/174/174 416/184/184 192/175/175 +f 192/175/175 416/184/184 660/104/104 +f 629/176/176 659/86/86 638/185/185 +f 629/176/176 601/186/186 567/177/177 +f 567/177/177 554/187/187 508/178/178 +f 508/178/178 533/188/188 460/179/179 +f 460/179/179 526/189/189 446/180/180 +f 446/180/180 517/190/190 436/181/181 +f 436/181/181 504/191/191 419/182/182 +f 419/182/182 500/192/192 413/183/183 +f 413/183/183 501/193/193 416/184/184 +f 416/184/184 501/193/193 660/104/104 +f 638/185/185 659/86/86 644/194/194 +f 638/185/185 642/195/195 601/186/186 +f 601/186/186 633/196/196 554/187/187 +f 554/187/187 612/197/197 533/188/188 +f 533/188/188 607/198/198 526/189/189 +f 526/189/189 605/199/199 517/190/190 +f 517/190/190 598/200/200 504/191/191 +f 504/191/191 594/201/201 500/192/192 +f 500/192/192 595/202/202 501/193/193 +f 501/193/193 595/202/202 660/104/104 +f 644/194/194 659/86/86 658/203/203 +f 642/195/195 658/203/203 657/204/204 +f 644/194/194 658/203/203 642/195/195 +f 633/196/196 657/204/204 656/205/205 +f 642/195/195 657/204/204 633/196/196 +f 612/197/197 656/205/205 655/206/206 +f 633/196/196 656/205/205 612/197/197 +f 607/198/198 655/206/206 654/207/207 +f 612/197/197 655/206/206 607/198/198 +f 607/198/198 653/208/208 605/199/199 +f 605/199/199 652/209/209 598/200/200 +f 594/201/201 652/209/209 649/210/210 +f 598/200/200 652/209/209 594/201/201 +f 594/201/201 651/211/211 595/202/202 +f 595/202/202 651/211/211 660/104/104 +f 658/203/203 659/86/86 700/212/212 +f 657/204/204 700/212/212 709/213/213 +f 658/203/203 700/212/212 657/204/204 +f 656/205/205 709/213/213 730/214/214 +f 657/204/204 709/213/213 656/205/205 +f 655/206/206 730/214/214 739/215/215 +f 656/205/205 730/214/214 655/206/206 +f 654/207/207 739/215/215 744/216/216 +f 655/206/206 739/215/215 654/207/207 +f 654/207/207 745/217/217 653/208/208 +f 653/208/208 751/218/218 652/209/209 +f 649/210/210 751/218/218 753/219/219 +f 652/209/209 751/218/218 649/210/210 +f 649/210/210 755/220/220 651/211/211 +f 651/211/211 755/220/220 660/104/104 +f 700/212/212 659/86/86 720/221/221 +f 700/212/212 749/222/222 709/213/213 +f 709/213/213 801/223/223 730/214/214 +f 730/214/214 823/224/224 739/215/215 +f 739/215/215 828/225/225 744/216/216 +f 744/216/216 840/226/226 745/217/217 +f 745/217/217 850/227/227 751/218/218 +f 751/218/218 852/228/228 753/219/219 +f 753/219/219 853/229/229 755/220/220 +f 755/220/220 660/104/104 853/229/229 +f 720/221/221 659/86/86 733/230/230 +f 720/221/221 790/231/231 749/222/222 +f 749/222/222 847/232/232 801/223/223 +f 801/223/223 894/233/233 823/224/224 +f 823/224/224 907/234/234 828/225/225 +f 828/225/225 916/235/235 840/226/226 +f 840/226/226 930/236/236 850/227/227 +f 850/227/227 936/237/237 852/228/228 +f 852/228/228 853/229/229 939/238/238 +f 853/229/229 660/104/104 939/238/238 +f 733/230/230 659/86/86 765/239/239 +f 733/230/230 825/240/240 790/231/231 +f 790/231/231 923/241/241 847/232/232 +f 847/232/232 1031/242/242 894/233/233 +f 894/233/233 1066/243/243 907/234/234 +f 907/234/234 1116/244/244 916/235/235 +f 916/235/235 1154/245/245 930/236/236 +f 930/236/236 1159/246/246 936/237/237 +f 936/237/237 939/238/238 1162/247/247 +f 939/238/238 660/104/104 1162/247/247 +f 765/239/239 659/86/86 776/248/248 +f 765/239/239 834/249/249 825/240/240 +f 825/240/240 946/250/250 923/241/241 +f 923/241/241 1069/251/251 1031/242/242 +f 1031/242/242 1130/252/252 1066/243/243 +f 1066/243/243 1164/253/253 1116/244/244 +f 1116/244/244 1199/254/254 1154/245/245 +f 1154/245/245 1202/255/255 1159/246/246 +f 1159/246/246 1204/1/1 1162/247/247 +f 1162/247/247 660/104/104 1204/1/1 +f 776/248/248 659/86/86 777/85/85 +f 776/248/248 835/89/89 834/249/249 +f 834/249/249 945/91/91 946/250/250 +f 946/250/250 1068/93/93 1069/251/251 +f 1069/251/251 1129/95/95 1130/252/252 +f 1130/252/252 1163/97/97 1164/253/253 +f 1164/253/253 1200/99/99 1199/254/254 +f 1199/254/254 1203/101/101 1202/255/255 +f 1204/1/1 1205/103/103 1203/101/101 +f 1202/255/255 1203/101/101 1204/1/1 +f 1204/1/1 660/104/104 1205/103/103 +f 777/85/85 766/87/87 824/88/88 +f 835/89/89 824/88/88 924/90/90 +f 945/91/91 924/90/90 1030/92/92 +f 1068/93/93 1030/92/92 1067/94/94 +f 1129/95/95 1067/94/94 1117/96/96 +f 1163/97/97 1117/96/96 1155/98/98 +f 1200/99/99 1155/98/98 1160/100/100 +f 1203/101/101 1160/100/100 1161/102/102 +f 766/87/87 734/105/105 789/106/106 +f 824/88/88 789/106/106 846/107/107 +f 924/90/90 846/107/107 895/108/108 +f 1030/92/92 895/108/108 906/109/109 +f 1067/94/94 906/109/109 917/110/110 +f 1117/96/96 917/110/110 931/111/111 +f 1155/98/98 931/111/111 937/112/112 +f 1160/100/100 937/112/112 938/113/113 +f 734/105/105 701/114/114 708/115/115 +f 789/106/106 708/115/115 731/116/116 +f 846/107/107 731/116/116 738/117/117 +f 895/108/108 738/117/117 743/118/118 +f 906/109/109 743/118/118 746/119/119 +f 917/110/110 746/119/119 752/120/120 +f 931/111/111 752/120/120 754/121/121 +f 937/112/112 754/121/121 756/122/122 +f 645/123/123 630/132/132 566/133/133 +f 641/124/124 566/133/133 507/134/134 +f 632/125/125 507/134/134 459/135/135 +f 611/126/126 459/135/135 445/136/136 +f 606/127/127 445/136/136 435/137/137 +f 604/128/128 435/137/137 420/138/138 +f 599/129/129 420/138/138 414/139/139 +f 596/130/130 415/140/140 414/139/139 +f 630/132/132 587/141/141 529/142/142 +f 566/133/133 529/142/142 427/143/143 +f 507/134/134 427/143/143 320/144/144 +f 459/135/135 320/144/144 288/145/145 +f 445/136/136 288/145/145 235/146/146 +f 435/137/137 235/146/146 197/147/147 +f 420/138/138 197/147/147 190/148/148 +f 414/139/139 191/149/149 190/148/148 +f 587/141/141 578/150/150 525/151/151 +f 529/142/142 525/151/151 408/152/152 +f 427/143/143 408/152/152 286/153/153 +f 320/144/144 286/153/153 221/154/154 +f 288/145/145 221/154/154 187/155/155 +f 235/146/146 187/155/155 154/156/156 +f 197/147/147 154/156/156 146/157/157 +f 190/148/148 146/157/157 147/158/158 +f 578/150/150 577/159/159 524/160/160 +f 525/151/151 524/160/160 409/161/161 +f 408/152/152 409/161/161 287/162/162 +f 286/153/153 287/162/162 222/163/163 +f 221/154/154 222/163/163 188/164/164 +f 187/155/155 188/164/164 153/165/165 +f 154/156/156 153/165/165 145/166/166 +f 577/159/159 586/167/167 530/168/168 +f 524/160/160 530/168/168 428/169/169 +f 409/161/161 428/169/169 321/170/170 +f 287/162/162 321/170/170 289/171/171 +f 222/163/163 289/171/171 236/172/172 +f 188/164/164 236/172/172 196/173/173 +f 153/165/165 196/173/173 189/174/174 +f 145/166/166 189/174/174 192/175/175 +f 586/167/167 629/176/176 567/177/177 +f 530/168/168 567/177/177 508/178/178 +f 428/169/169 508/178/178 460/179/179 +f 321/170/170 460/179/179 446/180/180 +f 289/171/171 446/180/180 436/181/181 +f 236/172/172 436/181/181 419/182/182 +f 196/173/173 419/182/182 413/183/183 +f 189/174/174 413/183/183 416/184/184 +f 629/176/176 638/185/185 601/186/186 +f 567/177/177 601/186/186 554/187/187 +f 508/178/178 554/187/187 533/188/188 +f 460/179/179 533/188/188 526/189/189 +f 446/180/180 526/189/189 517/190/190 +f 436/181/181 517/190/190 504/191/191 +f 419/182/182 504/191/191 500/192/192 +f 413/183/183 500/192/192 501/193/193 +f 638/185/185 644/194/194 642/195/195 +f 601/186/186 642/195/195 633/196/196 +f 554/187/187 633/196/196 612/197/197 +f 533/188/188 612/197/197 607/198/198 +f 526/189/189 607/198/198 605/199/199 +f 517/190/190 605/199/199 598/200/200 +f 504/191/191 598/200/200 594/201/201 +f 500/192/192 594/201/201 595/202/202 +f 607/198/198 654/207/207 653/208/208 +f 605/199/199 653/208/208 652/209/209 +f 594/201/201 649/210/210 651/211/211 +f 654/207/207 744/216/216 745/217/217 +f 653/208/208 745/217/217 751/218/218 +f 649/210/210 753/219/219 755/220/220 +f 700/212/212 720/221/221 749/222/222 +f 709/213/213 749/222/222 801/223/223 +f 730/214/214 801/223/223 823/224/224 +f 739/215/215 823/224/224 828/225/225 +f 744/216/216 828/225/225 840/226/226 +f 745/217/217 840/226/226 850/227/227 +f 751/218/218 850/227/227 852/228/228 +f 753/219/219 852/228/228 853/229/229 +f 720/221/221 733/230/230 790/231/231 +f 749/222/222 790/231/231 847/232/232 +f 801/223/223 847/232/232 894/233/233 +f 823/224/224 894/233/233 907/234/234 +f 828/225/225 907/234/234 916/235/235 +f 840/226/226 916/235/235 930/236/236 +f 850/227/227 930/236/236 936/237/237 +f 852/228/228 936/237/237 939/238/238 +f 733/230/230 765/239/239 825/240/240 +f 790/231/231 825/240/240 923/241/241 +f 847/232/232 923/241/241 1031/242/242 +f 894/233/233 1031/242/242 1066/243/243 +f 907/234/234 1066/243/243 1116/244/244 +f 916/235/235 1116/244/244 1154/245/245 +f 930/236/236 1154/245/245 1159/246/246 +f 936/237/237 1159/246/246 1162/247/247 +f 765/239/239 776/248/248 834/249/249 +f 825/240/240 834/249/249 946/250/250 +f 923/241/241 946/250/250 1069/251/251 +f 1031/242/242 1069/251/251 1130/252/252 +f 1066/243/243 1130/252/252 1164/253/253 +f 1116/244/244 1164/253/253 1199/254/254 +f 1154/245/245 1199/254/254 1202/255/255 +f 1159/246/246 1202/255/255 1204/1/1 +f 776/248/248 777/85/85 835/89/89 +f 834/249/249 835/89/89 945/91/91 +f 946/250/250 945/91/91 1068/93/93 +f 1069/251/251 1068/93/93 1129/95/95 +f 1130/252/252 1129/95/95 1163/97/97 +f 1164/253/253 1163/97/97 1200/99/99 +f 1199/254/254 1200/99/99 1203/101/101 +f 698/256/256 870/257/257 699/258/258 +f 699/258/258 1033/259/259 702/260/260 +f 702/260/260 1062/261/261 703/262/262 +f 703/262/262 1125/263/263 704/264/264 +f 704/264/264 1178/265/265 705/266/266 +f 705/266/266 1234/267/267 707/268/268 +f 707/268/268 1271/269/269 712/270/270 +f 712/270/270 1297/271/271 719/272/272 +f 719/272/272 1301/273/273 725/274/274 +f 725/274/274 1308/275/275 727/276/276 +f 727/276/276 1306/277/277 726/278/278 +f 726/278/278 1306/277/277 662/279/279 +f 829/280/280 1011/281/281 870/257/257 +f 870/257/257 1263/282/282 1033/259/259 +f 1033/259/259 1270/283/283 1062/261/261 +f 1062/261/261 1291/284/284 1125/263/263 +f 1125/263/263 1295/285/285 1178/265/265 +f 1178/265/265 1303/286/286 1234/267/267 +f 1234/267/267 1313/287/287 1271/269/269 +f 1271/269/269 1320/288/288 1297/271/271 +f 1297/271/271 1329/289/289 1301/273/273 +f 1301/273/273 1333/290/290 1308/275/275 +f 1308/275/275 1332/291/291 1306/277/277 +f 1306/277/277 1332/291/291 662/279/279 +f 922/292/292 1158/293/293 1011/281/281 +f 1011/281/281 1294/294/294 1263/282/282 +f 1263/282/282 1298/295/295 1270/283/283 +f 1270/283/283 1302/296/296 1291/284/284 +f 1291/284/284 1309/297/297 1295/285/285 +f 1295/285/285 1312/298/298 1303/286/286 +f 1303/286/286 1321/299/299 1313/287/287 +f 1313/287/287 1335/300/300 1320/288/288 +f 1320/288/288 1340/301/301 1329/289/289 +f 1329/289/289 1343/302/302 1333/290/290 +f 1333/290/290 1341/303/303 1332/291/291 +f 1332/291/291 1341/303/303 662/279/279 +f 1005/304/304 1158/293/293 1180/305/305 +f 1158/293/293 1294/294/294 1296/306/306 +f 1294/294/294 1298/295/295 1300/307/307 +f 1298/295/295 1302/296/296 1307/308/308 +f 1302/296/296 1309/297/297 1310/309/309 +f 1309/297/297 1312/298/298 1315/310/310 +f 1312/298/298 1321/299/299 1322/311/311 +f 1321/299/299 1335/300/300 1337/312/312 +f 1335/300/300 1340/301/301 1342/313/313 +f 1340/301/301 1343/302/302 1353/314/314 +f 1343/302/302 1341/303/303 1352/315/315 +f 1341/303/303 1352/315/315 662/279/279 +f 1180/305/305 184/316/316 342/317/317 +f 1038/318/318 1180/305/305 342/317/317 +f 1296/306/306 184/316/316 57/319/319 +f 1180/305/305 1296/306/306 184/316/316 +f 1300/307/307 57/319/319 54/320/320 +f 1296/306/306 1300/307/307 57/319/319 +f 1307/308/308 46/321/321 54/320/320 +f 1300/307/307 1307/308/308 54/320/320 +f 1310/309/309 44/322/322 46/321/321 +f 1307/308/308 1310/309/309 46/321/321 +f 1315/310/310 44/322/322 39/323/323 +f 1310/309/309 1315/310/310 44/322/322 +f 1322/311/311 32/324/324 39/323/323 +f 1315/310/310 1322/311/311 39/323/323 +f 1337/312/312 17/325/325 32/324/324 +f 1322/311/311 1337/312/312 32/324/324 +f 1337/312/312 1342/313/313 12/326/326 +f 1353/314/314 1/327/327 12/326/326 +f 1342/313/313 1353/314/314 12/326/326 +f 1/327/327 1352/315/315 2/328/328 +f 1/327/327 1353/314/314 1352/315/315 +f 1352/315/315 662/279/279 2/328/328 +f 342/317/317 184/316/316 204/329/329 +f 184/316/316 60/330/330 57/319/319 +f 57/319/319 55/331/331 54/320/320 +f 54/320/320 52/332/332 46/321/321 +f 46/321/321 45/333/333 44/322/322 +f 44/322/322 40/334/334 39/323/323 +f 39/323/323 33/335/335 32/324/324 +f 32/324/324 19/336/336 17/325/325 +f 17/325/325 14/337/337 12/326/326 +f 12/326/326 11/338/338 1/327/327 +f 1/327/327 2/328/328 13/339/339 +f 2/328/328 662/279/279 13/339/339 +f 353/340/340 349/341/341 204/329/329 +f 204/329/329 93/342/342 60/330/330 +f 60/330/330 85/343/343 55/331/331 +f 55/331/331 71/344/344 52/332/332 +f 52/332/332 59/345/345 45/333/333 +f 45/333/333 51/346/346 40/334/334 +f 40/334/334 41/347/347 33/335/335 +f 33/335/335 34/348/348 19/336/336 +f 19/336/336 27/349/349 14/337/337 +f 14/337/337 21/350/350 11/338/338 +f 11/338/338 22/351/351 13/339/339 +f 13/339/339 22/351/351 662/279/279 +f 431/352/352 492/353/353 349/341/341 +f 349/341/341 352/354/354 93/342/342 +f 93/342/342 313/355/355 85/343/343 +f 85/343/343 268/356/356 71/344/344 +f 71/344/344 202/357/357 59/345/345 +f 59/345/345 133/358/358 51/346/346 +f 51/346/346 87/359/359 41/347/347 +f 41/347/347 58/360/360 34/348/348 +f 34/348/348 53/361/361 27/349/349 +f 27/349/349 47/362/362 21/350/350 +f 21/350/350 48/363/363 22/351/351 +f 22/351/351 48/363/363 662/279/279 +f 531/364/364 699/258/258 492/353/353 +f 492/353/353 702/260/260 352/354/354 +f 352/354/354 703/262/262 313/355/355 +f 313/355/355 704/264/264 268/356/356 +f 268/356/356 705/266/266 202/357/357 +f 202/357/357 707/268/268 133/358/358 +f 133/358/358 712/270/270 87/359/359 +f 87/359/359 719/272/272 58/360/360 +f 58/360/360 725/274/274 53/361/361 +f 53/361/361 727/276/276 47/362/362 +f 47/362/362 726/278/278 48/363/363 +f 48/363/363 726/278/278 662/279/279 +f 698/256/256 829/280/280 870/257/257 +f 699/258/258 870/257/257 1033/259/259 +f 702/260/260 1033/259/259 1062/261/261 +f 703/262/262 1062/261/261 1125/263/263 +f 704/264/264 1125/263/263 1178/265/265 +f 705/266/266 1178/265/265 1234/267/267 +f 707/268/268 1234/267/267 1271/269/269 +f 712/270/270 1271/269/269 1297/271/271 +f 719/272/272 1297/271/271 1301/273/273 +f 725/274/274 1301/273/273 1308/275/275 +f 727/276/276 1308/275/275 1306/277/277 +f 829/280/280 922/292/292 1011/281/281 +f 870/257/257 1011/281/281 1263/282/282 +f 1033/259/259 1263/282/282 1270/283/283 +f 1062/261/261 1270/283/283 1291/284/284 +f 1125/263/263 1291/284/284 1295/285/285 +f 1178/265/265 1295/285/285 1303/286/286 +f 1234/267/267 1303/286/286 1313/287/287 +f 1271/269/269 1313/287/287 1320/288/288 +f 1297/271/271 1320/288/288 1329/289/289 +f 1301/273/273 1329/289/289 1333/290/290 +f 1308/275/275 1333/290/290 1332/291/291 +f 922/292/292 1158/293/293 1005/304/304 +f 1011/281/281 1294/294/294 1158/293/293 +f 1263/282/282 1298/295/295 1294/294/294 +f 1270/283/283 1302/296/296 1298/295/295 +f 1291/284/284 1309/297/297 1302/296/296 +f 1295/285/285 1312/298/298 1309/297/297 +f 1303/286/286 1321/299/299 1312/298/298 +f 1313/287/287 1335/300/300 1321/299/299 +f 1320/288/288 1340/301/301 1335/300/300 +f 1329/289/289 1343/302/302 1340/301/301 +f 1333/290/290 1343/302/302 1341/303/303 +f 1005/304/304 1180/305/305 1038/318/318 +f 1158/293/293 1296/306/306 1180/305/305 +f 1294/294/294 1300/307/307 1296/306/306 +f 1298/295/295 1307/308/308 1300/307/307 +f 1302/296/296 1310/309/309 1307/308/308 +f 1309/297/297 1315/310/310 1310/309/309 +f 1312/298/298 1322/311/311 1315/310/310 +f 1321/299/299 1337/312/312 1322/311/311 +f 1335/300/300 1342/313/313 1337/312/312 +f 1340/301/301 1353/314/314 1342/313/313 +f 1343/302/302 1352/315/315 1353/314/314 +f 1337/312/312 17/325/325 12/326/326 +f 342/317/317 353/340/340 204/329/329 +f 184/316/316 204/329/329 60/330/330 +f 57/319/319 60/330/330 55/331/331 +f 54/320/320 55/331/331 52/332/332 +f 46/321/321 52/332/332 45/333/333 +f 44/322/322 45/333/333 40/334/334 +f 39/323/323 40/334/334 33/335/335 +f 32/324/324 33/335/335 19/336/336 +f 17/325/325 19/336/336 14/337/337 +f 12/326/326 14/337/337 11/338/338 +f 1/327/327 11/338/338 13/339/339 +f 353/340/340 431/352/352 349/341/341 +f 204/329/329 349/341/341 93/342/342 +f 60/330/330 93/342/342 85/343/343 +f 55/331/331 85/343/343 71/344/344 +f 52/332/332 71/344/344 59/345/345 +f 45/333/333 59/345/345 51/346/346 +f 40/334/334 51/346/346 41/347/347 +f 33/335/335 41/347/347 34/348/348 +f 19/336/336 34/348/348 27/349/349 +f 14/337/337 27/349/349 21/350/350 +f 11/338/338 21/350/350 22/351/351 +f 431/352/352 531/364/364 492/353/353 +f 349/341/341 492/353/353 352/354/354 +f 93/342/342 352/354/354 313/355/355 +f 85/343/343 313/355/355 268/356/356 +f 71/344/344 268/356/356 202/357/357 +f 59/345/345 202/357/357 133/358/358 +f 51/346/346 133/358/358 87/359/359 +f 41/347/347 87/359/359 58/360/360 +f 34/348/348 58/360/360 53/361/361 +f 27/349/349 53/361/361 47/362/362 +f 21/350/350 47/362/362 48/363/363 +f 531/364/364 698/256/256 699/258/258 +f 492/353/353 699/258/258 702/260/260 +f 352/354/354 702/260/260 703/262/262 +f 313/355/355 703/262/262 704/264/264 +f 268/356/356 704/264/264 705/266/266 +f 202/357/357 705/266/266 707/268/268 +f 133/358/358 707/268/268 712/270/270 +f 87/359/359 712/270/270 719/272/272 +f 58/360/360 719/272/272 725/274/274 +f 53/361/361 725/274/274 727/276/276 +f 47/362/362 727/276/276 726/278/278 +f 1038/318/318 1180/305/305 1101/365/365 +f 1180/305/305 1296/306/306 1284/366/366 +f 1296/306/306 1300/307/307 1293/367/367 +f 1300/307/307 1307/308/308 1299/368/368 +f 1307/308/308 1310/309/309 1304/369/369 +f 1310/309/309 1315/310/310 1311/370/370 +f 1315/310/310 1322/311/311 1318/371/371 +f 1322/311/311 1337/312/312 1334/372/372 +f 1337/312/312 1342/313/313 1336/373/373 +f 1342/313/313 1353/314/314 1339/374/374 +f 1353/314/314 1352/315/315 1338/375/375 +f 1352/315/315 1338/375/375 662/279/279 +f 984/376/376 935/377/377 1101/365/365 +f 1101/365/365 1219/378/378 1284/366/366 +f 1284/366/366 1240/379/379 1293/367/367 +f 1293/367/367 1255/380/380 1299/368/368 +f 1299/368/368 1267/381/381 1304/369/369 +f 1304/369/369 1292/382/382 1311/370/370 +f 1311/370/370 1305/383/383 1318/371/371 +f 1318/371/371 1314/384/384 1334/372/372 +f 1334/372/372 1316/385/385 1336/373/373 +f 1336/373/373 1319/386/386 1339/374/374 +f 1339/374/374 1317/387/387 1338/375/375 +f 1338/375/375 1317/387/387 662/279/279 +f 887/388/388 799/389/389 935/377/377 +f 935/377/377 865/390/390 1219/378/378 +f 1219/378/378 874/391/391 1240/379/379 +f 1240/379/379 896/392/392 1255/380/380 +f 1255/380/380 908/393/393 1267/381/381 +f 1267/381/381 942/394/394 1292/382/382 +f 1292/382/382 1045/395/395 1305/383/383 +f 1305/383/383 1174/396/396 1314/384/384 +f 1314/384/384 1218/397/397 1316/385/385 +f 1316/385/385 1231/398/398 1319/386/386 +f 1319/386/386 1228/399/399 1317/387/387 +f 1317/387/387 1228/399/399 662/279/279 +f 778/400/400 564/401/401 799/389/389 +f 799/389/389 510/402/402 865/390/390 +f 874/391/391 510/402/402 493/403/403 +f 865/390/390 510/402/402 874/391/391 +f 896/392/392 493/403/403 483/404/404 +f 874/391/391 493/403/403 896/392/392 +f 908/393/393 483/404/404 465/405/405 +f 896/392/392 483/404/404 908/393/393 +f 942/394/394 465/405/405 441/406/406 +f 908/393/393 465/405/405 942/394/394 +f 1045/395/395 441/406/406 362/407/407 +f 942/394/394 441/406/406 1045/395/395 +f 1045/395/395 251/408/408 1174/396/396 +f 1174/396/396 193/409/409 1218/397/397 +f 1218/397/397 164/410/410 1231/398/398 +f 1228/399/399 164/410/410 171/411/411 +f 1231/398/398 164/410/410 1228/399/399 +f 1228/399/399 171/411/411 662/279/279 +f 585/412/412 429/413/413 564/401/401 +f 564/401/401 150/414/414 510/402/402 +f 510/402/402 125/415/415 493/403/403 +f 493/403/403 100/416/416 483/404/404 +f 483/404/404 92/417/417 465/405/405 +f 465/405/405 70/418/418 441/406/406 +f 441/406/406 49/419/419 362/407/407 +f 362/407/407 42/420/420 251/408/408 +f 251/408/408 38/421/421 193/409/409 +f 193/409/409 36/422/422 164/410/410 +f 164/410/410 37/423/423 171/411/411 +f 171/411/411 37/423/423 662/279/279 +f 477/424/424 264/425/425 429/413/413 +f 429/413/413 74/426/426 150/414/414 +f 150/414/414 61/427/427 125/415/415 +f 125/415/415 56/428/428 100/416/416 +f 100/416/416 50/429/429 92/417/417 +f 92/417/417 43/430/430 70/418/418 +f 70/418/418 35/431/431 49/419/419 +f 49/419/419 20/432/432 42/420/420 +f 42/420/420 18/433/433 38/421/421 +f 38/421/421 15/434/434 36/422/422 +f 36/422/422 16/435/435 37/423/423 +f 37/423/423 16/435/435 662/279/279 +f 381/436/436 184/316/316 264/425/425 +f 264/425/425 57/319/319 74/426/426 +f 74/426/426 54/320/320 61/427/427 +f 61/427/427 46/321/321 56/428/428 +f 56/428/428 44/322/322 50/429/429 +f 50/429/429 39/323/323 43/430/430 +f 43/430/430 32/324/324 35/431/431 +f 35/431/431 17/325/325 20/432/432 +f 20/432/432 12/326/326 18/433/433 +f 1/327/327 15/434/434 18/433/433 +f 15/434/434 2/328/328 16/435/435 +f 16/435/435 2/328/328 662/279/279 +f 1038/318/318 984/376/376 1101/365/365 +f 1180/305/305 1101/365/365 1284/366/366 +f 1296/306/306 1284/366/366 1293/367/367 +f 1300/307/307 1293/367/367 1299/368/368 +f 1307/308/308 1299/368/368 1304/369/369 +f 1310/309/309 1304/369/369 1311/370/370 +f 1315/310/310 1311/370/370 1318/371/371 +f 1322/311/311 1318/371/371 1334/372/372 +f 1337/312/312 1334/372/372 1336/373/373 +f 1342/313/313 1336/373/373 1339/374/374 +f 1353/314/314 1339/374/374 1338/375/375 +f 984/376/376 887/388/388 935/377/377 +f 1101/365/365 935/377/377 1219/378/378 +f 1284/366/366 1219/378/378 1240/379/379 +f 1293/367/367 1240/379/379 1255/380/380 +f 1299/368/368 1255/380/380 1267/381/381 +f 1304/369/369 1267/381/381 1292/382/382 +f 1311/370/370 1292/382/382 1305/383/383 +f 1318/371/371 1305/383/383 1314/384/384 +f 1334/372/372 1314/384/384 1316/385/385 +f 1336/373/373 1316/385/385 1319/386/386 +f 1339/374/374 1319/386/386 1317/387/387 +f 887/388/388 778/400/400 799/389/389 +f 935/377/377 799/389/389 865/390/390 +f 1219/378/378 865/390/390 874/391/391 +f 1240/379/379 874/391/391 896/392/392 +f 1255/380/380 896/392/392 908/393/393 +f 1267/381/381 908/393/393 942/394/394 +f 1292/382/382 942/394/394 1045/395/395 +f 1305/383/383 1045/395/395 1174/396/396 +f 1314/384/384 1174/396/396 1218/397/397 +f 1316/385/385 1218/397/397 1231/398/398 +f 1319/386/386 1231/398/398 1228/399/399 +f 778/400/400 585/412/412 564/401/401 +f 799/389/389 564/401/401 510/402/402 +f 1045/395/395 362/407/407 251/408/408 +f 1174/396/396 251/408/408 193/409/409 +f 1218/397/397 193/409/409 164/410/410 +f 585/412/412 477/424/424 429/413/413 +f 564/401/401 429/413/413 150/414/414 +f 510/402/402 150/414/414 125/415/415 +f 493/403/403 125/415/415 100/416/416 +f 483/404/404 100/416/416 92/417/417 +f 465/405/405 92/417/417 70/418/418 +f 441/406/406 70/418/418 49/419/419 +f 362/407/407 49/419/419 42/420/420 +f 251/408/408 42/420/420 38/421/421 +f 193/409/409 38/421/421 36/422/422 +f 164/410/410 36/422/422 37/423/423 +f 477/424/424 381/436/436 264/425/425 +f 429/413/413 264/425/425 74/426/426 +f 150/414/414 74/426/426 61/427/427 +f 125/415/415 61/427/427 56/428/428 +f 100/416/416 56/428/428 50/429/429 +f 92/417/417 50/429/429 43/430/430 +f 70/418/418 43/430/430 35/431/431 +f 49/419/419 35/431/431 20/432/432 +f 42/420/420 20/432/432 18/433/433 +f 38/421/421 18/433/433 15/434/434 +f 36/422/422 15/434/434 16/435/435 +f 381/436/436 342/317/317 184/316/316 +f 264/425/425 184/316/316 57/319/319 +f 74/426/426 57/319/319 54/320/320 +f 61/427/427 54/320/320 46/321/321 +f 56/428/428 46/321/321 44/322/322 +f 50/429/429 44/322/322 39/323/323 +f 43/430/430 39/323/323 32/324/324 +f 35/431/431 32/324/324 17/325/325 +f 20/432/432 17/325/325 12/326/326 +f 18/433/433 12/326/326 1/327/327 +f 1/327/327 2/328/328 15/434/434 +f 648/437/437 759/52/52 756/122/122 +f 650/438/438 648/437/437 756/122/122 +f 756/122/122 759/52/52 867/50/50 +f 854/439/439 867/50/50 978/48/48 +f 938/113/113 978/48/48 1108/46/46 +f 1057/440/440 1108/46/46 1207/42/42 +f 1161/102/102 1207/42/42 1237/41/41 +f 1205/103/103 1237/41/41 1236/2/2 +f 756/122/122 867/50/50 854/439/439 +f 854/439/439 978/48/48 938/113/113 +f 938/113/113 1108/46/46 1057/440/440 +f 1057/440/440 1207/42/42 1161/102/102 +f 1161/102/102 1237/41/41 1205/103/103 +f 1205/103/103 1236/2/2 1204/1/1 +f 692/441/441 597/131/131 592/54/54 +f 691/442/442 597/131/131 692/441/441 +f 597/131/131 486/56/56 592/54/54 +f 502/443/443 377/57/57 486/56/56 +f 415/140/140 246/58/58 377/57/57 +f 297/444/444 143/59/59 246/58/58 +f 191/149/149 118/60/60 143/59/59 +f 147/158/158 117/15/15 118/60/60 +f 597/131/131 502/443/443 486/56/56 +f 502/443/443 415/140/140 377/57/57 +f 415/140/140 297/444/444 246/58/58 +f 297/444/444 191/149/149 143/59/59 +f 191/149/149 147/158/158 118/60/60 +f 147/158/158 148/16/16 117/15/15 +f 148/16/16 142/62/62 117/15/15 +f 192/175/175 245/64/64 142/62/62 +f 298/445/445 376/66/66 245/64/64 +f 416/184/184 485/68/68 376/66/66 +f 501/193/193 591/70/70 485/68/68 +f 595/202/202 647/72/72 591/70/70 +f 148/16/16 192/175/175 142/62/62 +f 192/175/175 298/445/445 245/64/64 +f 298/445/445 416/184/184 376/66/66 +f 416/184/184 501/193/193 485/68/68 +f 501/193/193 595/202/202 591/70/70 +f 595/202/202 651/211/211 647/72/72 +f 651/211/211 758/74/74 647/72/72 +f 755/220/220 866/76/76 758/74/74 +f 853/229/229 977/78/78 866/76/76 +f 939/238/238 1107/80/80 977/78/78 +f 1056/446/446 1206/82/82 1107/80/80 +f 1162/247/247 1236/2/2 1206/82/82 +f 651/211/211 755/220/220 758/74/74 +f 755/220/220 853/229/229 866/76/76 +f 853/229/229 939/238/238 977/78/78 +f 939/238/238 1056/446/446 1107/80/80 +f 1056/446/446 1162/247/247 1206/82/82 +f 1162/247/247 1204/1/1 1236/2/2 +f 97/447/447 78/448/448 77/449/448 +f 98/450/449 78/448/448 97/447/447 +f 77/449/448 80/451/450 79/452/450 +f 78/448/448 80/451/450 77/449/448 +f 79/452/450 31/453/451 30/454/451 +f 80/451/450 31/453/451 79/452/450 +f 30/454/451 29/455/452 28/456/452 +f 31/453/451 29/455/452 30/454/451 +f 28/456/452 98/450/449 97/447/447 +f 29/455/452 98/450/449 28/456/452 +f 25/457/453 24/458/454 23/459/454 +f 26/460/453 24/458/454 25/457/453 +f 23/459/454 10/461/455 9/462/455 +f 24/458/454 10/461/455 23/459/454 +f 9/462/455 4/463/456 3/464/457 +f 10/461/455 4/463/456 9/462/455 +f 3/464/457 8/465/458 7/466/459 +f 4/463/456 8/465/458 3/464/457 +f 7/466/459 6/467/460 5/468/460 +f 8/465/458 6/467/460 7/466/459 +f 5/468/460 26/460/453 25/457/453 +f 6/467/460 26/460/453 5/468/460 +f 1328/469/461 1330/470/462 1331/471/462 +f 1327/472/461 1330/470/462 1328/469/461 +f 1331/471/462 1344/473/463 1345/474/463 +f 1330/470/462 1344/473/463 1331/471/462 +f 1345/474/463 1350/475/464 1351/476/464 +f 1344/473/463 1350/475/464 1345/474/463 +f 1351/476/464 1346/477/465 1347/478/465 +f 1350/475/464 1346/477/465 1351/476/464 +f 1347/478/465 1348/479/466 1349/480/466 +f 1346/477/465 1348/479/466 1347/478/465 +f 1349/480/466 1327/472/461 1328/469/461 +f 1348/479/466 1327/472/461 1349/480/466 +f 1257/481/467 1274/482/468 1275/483/468 +f 1256/484/467 1274/482/468 1257/481/467 +f 1275/483/468 1276/485/469 1277/486/469 +f 1274/482/468 1276/485/469 1275/483/468 +f 1277/486/469 1323/487/470 1324/488/470 +f 1276/485/469 1323/487/470 1277/486/469 +f 1324/488/470 1325/489/471 1326/490/471 +f 1323/487/470 1325/489/471 1324/488/470 +f 1326/490/471 1256/484/467 1257/481/467 +f 1325/489/471 1256/484/467 1326/490/471 +f 91/491/472 99/492/473 141/493/474 +f 99/492/473 108/494/475 157/495/476 +f 108/494/475 149/496/477 203/497/478 +f 149/496/477 258/498/479 292/499/480 +f 258/498/479 329/500/481 335/501/482 +f 124/502/483 141/493/474 198/503/484 +f 141/493/474 157/495/476 209/504/485 +f 157/495/476 203/497/478 261/505/486 +f 203/497/478 292/499/480 299/506/487 +f 335/501/482 338/507/488 299/506/487 +f 292/499/480 335/501/482 299/506/487 +f 182/508/489 198/503/484 412/509/490 +f 198/503/484 209/504/485 407/510/491 +f 209/504/485 261/505/486 388/511/492 +f 261/505/486 299/506/487 350/512/493 +f 299/506/487 338/507/488 337/513/494 +f 437/514/495 412/509/490 565/515/496 +f 412/509/490 407/510/491 540/516/497 +f 407/510/491 388/511/492 489/517/498 +f 350/512/493 405/518/499 489/517/498 +f 388/511/492 350/512/493 489/517/498 +f 350/512/493 337/513/494 328/519/500 +f 565/515/496 109/520/501 104/521/502 +f 643/522/503 565/515/496 104/521/502 +f 540/516/497 116/523/504 109/520/501 +f 565/515/496 540/516/497 109/520/501 +f 489/517/498 155/524/505 116/523/504 +f 540/516/497 489/517/498 116/523/504 +f 405/518/499 265/525/506 155/524/505 +f 489/517/498 405/518/499 155/524/505 +f 328/519/500 325/526/507 265/525/506 +f 405/518/499 328/519/500 265/525/506 +f 104/521/502 109/520/501 84/527/508 +f 109/520/501 116/523/504 89/528/509 +f 116/523/504 155/524/505 110/529/510 +f 155/524/505 265/525/506 227/530/511 +f 325/526/507 333/531/512 227/530/511 +f 265/525/506 325/526/507 227/530/511 +f 75/532/513 84/527/508 82/533/514 +f 84/527/508 89/528/509 86/534/515 +f 89/528/509 110/529/510 106/535/516 +f 110/529/510 227/530/511 216/536/517 +f 227/530/511 333/531/512 336/537/518 +f 66/538/519 82/533/514 83/539/520 +f 82/533/514 86/534/515 90/540/521 +f 86/534/515 106/535/516 111/541/522 +f 106/535/516 216/536/517 225/542/523 +f 216/536/517 336/537/518 334/543/524 +f 72/544/525 83/539/520 88/545/526 +f 83/539/520 90/540/521 96/546/527 +f 90/540/521 111/541/522 123/547/528 +f 111/541/522 225/542/523 240/548/529 +f 225/542/523 334/543/524 332/549/530 +f 81/550/531 88/545/526 99/492/473 +f 88/545/526 96/546/527 108/494/475 +f 96/546/527 123/547/528 149/496/477 +f 123/547/528 240/548/529 258/498/479 +f 240/548/529 332/549/530 329/500/481 +f 91/491/472 141/493/474 124/502/483 +f 99/492/473 157/495/476 141/493/474 +f 108/494/475 203/497/478 157/495/476 +f 149/496/477 292/499/480 203/497/478 +f 258/498/479 335/501/482 292/499/480 +f 124/502/483 198/503/484 182/508/489 +f 141/493/474 209/504/485 198/503/484 +f 157/495/476 261/505/486 209/504/485 +f 203/497/478 299/506/487 261/505/486 +f 182/508/489 412/509/490 437/514/495 +f 198/503/484 407/510/491 412/509/490 +f 209/504/485 388/511/492 407/510/491 +f 261/505/486 350/512/493 388/511/492 +f 299/506/487 337/513/494 350/512/493 +f 437/514/495 565/515/496 643/522/503 +f 412/509/490 540/516/497 565/515/496 +f 407/510/491 489/517/498 540/516/497 +f 350/512/493 328/519/500 405/518/499 +f 104/521/502 84/527/508 75/532/513 +f 109/520/501 89/528/509 84/527/508 +f 116/523/504 110/529/510 89/528/509 +f 155/524/505 227/530/511 110/529/510 +f 75/532/513 82/533/514 66/538/519 +f 84/527/508 86/534/515 82/533/514 +f 89/528/509 106/535/516 86/534/515 +f 110/529/510 216/536/517 106/535/516 +f 227/530/511 336/537/518 216/536/517 +f 66/538/519 83/539/520 72/544/525 +f 82/533/514 90/540/521 83/539/520 +f 86/534/515 111/541/522 90/540/521 +f 106/535/516 225/542/523 111/541/522 +f 216/536/517 334/543/524 225/542/523 +f 72/544/525 88/545/526 81/550/531 +f 83/539/520 96/546/527 88/545/526 +f 90/540/521 123/547/528 96/546/527 +f 111/541/522 240/548/529 123/547/528 +f 225/542/523 332/549/530 240/548/529 +f 81/550/531 99/492/473 91/491/472 +f 88/545/526 108/494/475 99/492/473 +f 96/546/527 149/496/477 108/494/475 +f 123/547/528 258/498/479 149/496/477 +f 240/548/529 329/500/481 258/498/479 +f 1261/551/532 1209/552/533 1254/553/534 +f 1254/553/534 1196/554/535 1246/555/536 +f 1246/555/536 1149/556/537 1201/557/538 +f 1201/557/538 1063/558/539 1094/559/540 +f 1094/559/540 1017/560/541 1023/561/542 +f 1226/562/543 1153/563/544 1209/552/533 +f 1209/552/533 1146/564/545 1196/554/535 +f 1196/554/535 1091/565/546 1149/556/537 +f 1149/556/537 1055/566/547 1063/558/539 +f 1017/560/541 1055/566/547 1014/567/548 +f 1063/558/539 1055/566/547 1017/560/541 +f 1169/568/549 941/569/550 1153/563/544 +f 1153/563/544 947/570/551 1146/564/545 +f 1146/564/545 968/571/552 1091/565/546 +f 1091/565/546 1002/572/553 1055/566/547 +f 1055/566/547 1015/573/554 1014/567/548 +f 915/574/555 791/575/556 941/569/550 +f 941/569/550 816/576/557 947/570/551 +f 947/570/551 863/577/558 968/571/552 +f 1002/572/553 863/577/558 949/578/559 +f 968/571/552 863/577/558 1002/572/553 +f 1002/572/553 1024/579/560 1015/573/554 +f 791/575/556 1249/580/561 1245/581/562 +f 706/582/563 1249/580/561 791/575/556 +f 816/576/557 1245/581/562 1239/583/564 +f 791/575/556 1245/581/562 816/576/557 +f 863/577/558 1239/583/564 1197/584/565 +f 816/576/557 1239/583/564 863/577/558 +f 949/578/559 1197/584/565 1088/585/566 +f 863/577/558 1197/584/565 949/578/559 +f 1024/579/560 1088/585/566 1027/586/567 +f 949/578/559 1088/585/566 1024/579/560 +f 1249/580/561 1268/587/568 1245/581/562 +f 1245/581/562 1264/588/569 1239/583/564 +f 1239/583/564 1244/589/570 1197/584/565 +f 1197/584/565 1124/590/571 1088/585/566 +f 1027/586/567 1124/590/571 1019/591/572 +f 1088/585/566 1124/590/571 1027/586/567 +f 1278/592/573 1272/593/574 1268/587/568 +f 1268/587/568 1266/594/575 1264/588/569 +f 1264/588/569 1248/595/576 1244/589/570 +f 1244/589/570 1137/596/577 1124/590/571 +f 1124/590/571 1016/597/578 1019/591/572 +f 1285/598/579 1269/599/580 1272/593/574 +f 1272/593/574 1262/600/581 1266/594/575 +f 1266/594/575 1243/601/582 1248/595/576 +f 1248/595/576 1126/602/583 1137/596/577 +f 1137/596/577 1018/603/584 1016/597/578 +f 1280/604/585 1265/605/586 1269/599/580 +f 1269/599/580 1258/606/587 1262/600/581 +f 1262/600/581 1227/607/588 1243/601/582 +f 1243/601/582 1114/608/589 1126/602/583 +f 1126/602/583 1020/609/590 1018/603/584 +f 1273/610/591 1254/553/534 1265/605/586 +f 1265/605/586 1246/555/536 1258/606/587 +f 1258/606/587 1201/557/538 1227/607/588 +f 1227/607/588 1094/559/540 1114/608/589 +f 1114/608/589 1023/561/542 1020/609/590 +f 1261/551/532 1226/562/543 1209/552/533 +f 1254/553/534 1209/552/533 1196/554/535 +f 1246/555/536 1196/554/535 1149/556/537 +f 1201/557/538 1149/556/537 1063/558/539 +f 1094/559/540 1063/558/539 1017/560/541 +f 1226/562/543 1169/568/549 1153/563/544 +f 1209/552/533 1153/563/544 1146/564/545 +f 1196/554/535 1146/564/545 1091/565/546 +f 1149/556/537 1091/565/546 1055/566/547 +f 1169/568/549 915/574/555 941/569/550 +f 1153/563/544 941/569/550 947/570/551 +f 1146/564/545 947/570/551 968/571/552 +f 1091/565/546 968/571/552 1002/572/553 +f 1055/566/547 1002/572/553 1015/573/554 +f 915/574/555 706/582/563 791/575/556 +f 941/569/550 791/575/556 816/576/557 +f 947/570/551 816/576/557 863/577/558 +f 1002/572/553 949/578/559 1024/579/560 +f 1249/580/561 1278/592/573 1268/587/568 +f 1245/581/562 1268/587/568 1264/588/569 +f 1239/583/564 1264/588/569 1244/589/570 +f 1197/584/565 1244/589/570 1124/590/571 +f 1278/592/573 1285/598/579 1272/593/574 +f 1268/587/568 1272/593/574 1266/594/575 +f 1264/588/569 1266/594/575 1248/595/576 +f 1244/589/570 1248/595/576 1137/596/577 +f 1124/590/571 1137/596/577 1016/597/578 +f 1285/598/579 1280/604/585 1269/599/580 +f 1272/593/574 1269/599/580 1262/600/581 +f 1266/594/575 1262/600/581 1243/601/582 +f 1248/595/576 1243/601/582 1126/602/583 +f 1137/596/577 1126/602/583 1018/603/584 +f 1280/604/585 1273/610/591 1265/605/586 +f 1269/599/580 1265/605/586 1258/606/587 +f 1262/600/581 1258/606/587 1227/607/588 +f 1243/601/582 1227/607/588 1114/608/589 +f 1126/602/583 1114/608/589 1020/609/590 +f 1273/610/591 1261/551/532 1254/553/534 +f 1265/605/586 1254/553/534 1246/555/536 +f 1258/606/587 1246/555/536 1201/557/538 +f 1227/607/588 1201/557/538 1094/559/540 +f 1114/608/589 1094/559/540 1023/561/542 +f 615/611/592 576/612/593 618/613/594 +f 774/614/595 618/613/594 775/615/594 +f 615/611/592 618/613/594 774/614/595 +f 774/614/595 775/615/594 800/616/596 +f 618/613/594 576/612/593 623/617/597 +f 775/615/594 623/617/597 757/618/598 +f 618/613/594 623/617/597 775/615/594 +f 775/615/594 757/618/598 800/616/596 +f 623/617/597 576/612/593 625/619/599 +f 757/618/598 625/619/599 722/620/600 +f 623/617/597 625/619/599 757/618/598 +f 757/618/598 722/620/600 800/616/596 +f 625/619/599 576/612/593 627/621/601 +f 722/620/600 627/621/601 717/622/602 +f 625/619/599 627/621/601 722/620/600 +f 722/620/600 717/622/602 800/616/596 +f 627/621/601 576/612/593 626/623/603 +f 717/622/602 626/623/603 715/624/604 +f 627/621/601 626/623/603 717/622/602 +f 717/622/602 715/624/604 800/616/596 +f 626/623/603 576/612/593 624/625/605 +f 715/624/604 624/625/605 713/626/606 +f 626/623/603 624/625/605 715/624/604 +f 715/624/604 713/626/606 800/616/596 +f 624/625/605 576/612/593 622/627/607 +f 713/626/606 622/627/607 714/628/608 +f 624/625/605 622/627/607 713/626/606 +f 713/626/606 714/628/608 800/616/596 +f 622/627/607 576/612/593 621/629/609 +f 714/628/608 621/629/609 716/630/610 +f 622/627/607 621/629/609 714/628/608 +f 714/628/608 716/630/610 800/616/596 +f 621/629/609 576/612/593 620/631/611 +f 716/630/610 620/631/611 718/632/612 +f 621/629/609 620/631/611 716/630/610 +f 716/630/610 718/632/612 800/616/596 +f 620/631/611 576/612/593 619/633/613 +f 718/632/612 619/633/613 721/634/614 +f 620/631/611 619/633/613 718/632/612 +f 718/632/612 721/634/614 800/616/596 +f 619/633/613 576/612/593 617/635/615 +f 721/634/614 617/635/615 764/636/616 +f 619/633/613 617/635/615 721/634/614 +f 721/634/614 764/636/616 800/616/596 +f 617/635/615 576/612/593 616/637/617 +f 764/636/616 616/637/617 767/638/618 +f 617/635/615 616/637/617 764/636/616 +f 764/636/616 767/638/618 800/616/596 +f 616/637/617 576/612/593 615/611/592 +f 767/638/618 615/611/592 774/614/595 +f 616/637/617 615/611/592 767/638/618 +f 767/638/618 774/614/595 800/616/596 +f 951/639/619 963/640/620 934/641/621 +f 973/642/622 934/641/621 893/643/623 +f 951/639/619 934/641/621 973/642/622 +f 983/644/624 893/643/623 872/645/625 +f 973/642/622 893/643/623 983/644/624 +f 986/646/626 872/645/625 876/647/627 +f 983/644/624 872/645/625 986/646/626 +f 988/648/628 876/647/627 898/649/628 +f 986/646/626 876/647/627 988/648/628 +f 934/641/621 963/640/620 920/650/629 +f 893/643/623 920/650/629 833/651/630 +f 934/641/621 920/650/629 893/643/623 +f 872/645/625 833/651/630 804/652/631 +f 893/643/623 833/651/630 872/645/625 +f 876/647/627 804/652/631 808/653/632 +f 872/645/625 804/652/631 876/647/627 +f 898/649/628 808/653/632 831/654/628 +f 876/647/627 808/653/632 898/649/628 +f 920/650/629 963/640/620 912/655/633 +f 833/651/630 912/655/633 819/656/634 +f 920/650/629 912/655/633 833/651/630 +f 804/652/631 819/656/634 783/657/635 +f 833/651/630 819/656/634 804/652/631 +f 808/653/632 783/657/635 788/658/636 +f 804/652/631 783/657/635 808/653/632 +f 831/654/628 788/658/636 811/659/628 +f 808/653/632 788/658/636 831/654/628 +f 912/655/633 963/640/620 919/660/637 +f 819/656/634 919/660/637 832/661/638 +f 912/655/633 919/660/637 819/656/634 +f 783/657/635 832/661/638 803/662/639 +f 819/656/634 832/661/638 783/657/635 +f 788/658/636 803/662/639 807/663/640 +f 783/657/635 803/662/639 788/658/636 +f 811/659/628 807/663/640 830/664/628 +f 788/658/636 807/663/640 811/659/628 +f 919/660/637 963/640/620 933/665/641 +f 832/661/638 933/665/641 892/666/642 +f 919/660/637 933/665/641 832/661/638 +f 803/662/639 892/666/642 871/667/643 +f 832/661/638 892/666/642 803/662/639 +f 807/663/640 871/667/643 875/668/644 +f 803/662/639 871/667/643 807/663/640 +f 830/664/628 875/668/644 897/669/628 +f 807/663/640 875/668/644 830/664/628 +f 933/665/641 963/640/620 950/670/645 +f 892/666/642 950/670/645 972/671/646 +f 933/665/641 950/670/645 892/666/642 +f 871/667/643 972/671/646 982/672/647 +f 892/666/642 972/671/646 871/667/643 +f 875/668/644 982/672/647 985/673/648 +f 871/667/643 982/672/647 875/668/644 +f 897/669/628 985/673/648 987/674/628 +f 875/668/644 985/673/648 897/669/628 +f 950/670/645 963/640/620 995/675/649 +f 972/671/646 995/675/649 1082/676/650 +f 950/670/645 995/675/649 972/671/646 +f 982/672/647 1082/676/650 1138/677/651 +f 972/671/646 1082/676/650 982/672/647 +f 985/673/648 1138/677/651 1140/678/652 +f 982/672/647 1138/677/651 985/673/648 +f 987/674/628 1140/678/652 1102/679/628 +f 985/673/648 1140/678/652 987/674/628 +f 995/675/649 963/640/620 1006/680/653 +f 1082/676/650 1006/680/653 1165/681/654 +f 995/675/649 1006/680/653 1082/676/650 +f 1138/677/651 1165/681/654 1222/682/655 +f 1082/676/650 1165/681/654 1138/677/651 +f 1140/678/652 1222/682/655 1220/683/656 +f 1138/677/651 1222/682/655 1140/678/652 +f 1102/679/628 1220/683/656 1192/684/657 +f 1140/678/652 1220/683/656 1102/679/628 +f 1006/680/653 963/640/620 1010/685/658 +f 1165/681/654 1010/685/658 1191/686/659 +f 1006/680/653 1010/685/658 1165/681/654 +f 1222/682/655 1191/686/659 1235/687/660 +f 1165/681/654 1191/686/659 1222/682/655 +f 1220/683/656 1235/687/660 1233/688/661 +f 1222/682/655 1235/687/660 1220/683/656 +f 1192/684/657 1233/688/661 1213/689/628 +f 1220/683/656 1233/688/661 1192/684/657 +f 1010/685/658 963/640/620 1007/690/662 +f 1191/686/659 1007/690/662 1166/691/663 +f 1010/685/658 1007/690/662 1191/686/659 +f 1235/687/660 1166/691/663 1223/692/664 +f 1191/686/659 1166/691/663 1235/687/660 +f 1233/688/661 1223/692/664 1221/693/665 +f 1235/687/660 1223/692/664 1233/688/661 +f 1213/689/628 1221/693/665 1193/694/628 +f 1233/688/661 1221/693/665 1213/689/628 +f 1007/690/662 963/640/620 996/695/666 +f 1166/691/663 996/695/666 1083/696/667 +f 1007/690/662 996/695/666 1166/691/663 +f 1223/692/664 1083/696/667 1139/697/668 +f 1166/691/663 1083/696/667 1223/692/664 +f 1221/693/665 1139/697/668 1141/698/669 +f 1223/692/664 1139/697/668 1221/693/665 +f 1193/694/628 1141/698/669 1103/699/628 +f 1221/693/665 1141/698/669 1193/694/628 +f 996/695/666 963/640/620 951/639/619 +f 1083/696/667 951/639/619 973/642/622 +f 996/695/666 951/639/619 1083/696/667 +f 1139/697/668 973/642/622 983/644/624 +f 1083/696/667 973/642/622 1139/697/668 +f 1141/698/669 983/644/624 986/646/626 +f 1139/697/668 983/644/624 1141/698/669 +f 1103/699/628 986/646/626 988/648/628 +f 1141/698/669 986/646/626 1103/699/628 +f 387/700/670 400/701/671 359/702/672 +f 375/703/673 359/702/672 271/704/674 +f 387/700/670 359/702/672 375/703/673 +f 368/705/675 271/704/674 213/706/676 +f 375/703/673 271/704/674 368/705/675 +f 364/707/677 213/706/676 206/708/678 +f 368/705/675 213/706/676 364/707/677 +f 366/709/679 206/708/678 238/710/679 +f 364/707/677 206/708/678 366/709/679 +f 359/702/672 400/701/671 346/711/680 +f 271/704/674 346/711/680 181/712/681 +f 359/702/672 346/711/680 271/704/674 +f 213/706/676 181/712/681 129/713/682 +f 271/704/674 181/712/681 213/706/676 +f 206/708/678 129/713/682 131/714/683 +f 213/706/676 129/713/682 206/708/678 +f 238/710/679 131/714/683 152/715/679 +f 206/708/678 131/714/683 238/710/679 +f 346/711/680 400/701/671 339/716/684 +f 181/712/681 339/716/684 159/717/685 +f 346/711/680 339/716/684 181/712/681 +f 129/713/682 159/717/685 112/718/686 +f 181/712/681 159/717/685 129/713/682 +f 131/714/683 112/718/686 115/719/687 +f 129/713/682 112/718/686 131/714/683 +f 152/715/679 115/719/687 132/720/679 +f 131/714/683 115/719/687 152/715/679 +f 339/716/684 400/701/671 345/721/688 +f 159/717/685 345/721/688 180/722/689 +f 339/716/684 345/721/688 159/717/685 +f 112/718/686 180/722/689 128/723/690 +f 159/717/685 180/722/689 112/718/686 +f 115/719/687 128/723/690 130/724/691 +f 112/718/686 128/723/690 115/719/687 +f 132/720/679 130/724/691 151/725/692 +f 115/719/687 130/724/691 132/720/679 +f 345/721/688 400/701/671 358/726/693 +f 180/722/689 358/726/693 270/727/694 +f 345/721/688 358/726/693 180/722/689 +f 128/723/690 270/727/694 212/728/695 +f 180/722/689 270/727/694 128/723/690 +f 130/724/691 212/728/695 205/729/696 +f 128/723/690 212/728/695 130/724/691 +f 151/725/692 205/729/696 237/730/679 +f 130/724/691 205/729/696 151/725/692 +f 358/726/693 400/701/671 386/731/697 +f 270/727/694 386/731/697 374/732/698 +f 358/726/693 386/731/697 270/727/694 +f 212/728/695 374/732/698 367/733/699 +f 270/727/694 374/732/698 212/728/695 +f 205/729/696 367/733/699 363/734/700 +f 212/728/695 367/733/699 205/729/696 +f 237/730/679 363/734/700 365/735/679 +f 205/729/696 363/734/700 237/730/679 +f 386/731/697 400/701/671 417/736/701 +f 374/732/698 417/736/701 455/737/702 +f 386/731/697 417/736/701 374/732/698 +f 367/733/699 455/737/702 475/738/703 +f 374/732/698 455/737/702 367/733/699 +f 363/734/700 475/738/703 469/739/704 +f 367/733/699 475/738/703 363/734/700 +f 365/735/679 469/739/704 452/740/705 +f 363/734/700 469/739/704 365/735/679 +f 417/736/701 400/701/671 432/741/706 +f 455/737/702 432/741/706 511/742/707 +f 417/736/701 432/741/706 455/737/702 +f 475/738/703 511/742/707 547/743/708 +f 455/737/702 511/742/707 475/738/703 +f 469/739/704 547/743/708 545/744/709 +f 475/738/703 547/743/708 469/739/704 +f 452/740/705 545/744/709 518/745/679 +f 469/739/704 545/744/709 452/740/705 +f 432/741/706 400/701/671 440/746/710 +f 511/742/707 440/746/710 532/747/711 +f 432/741/706 440/746/710 511/742/707 +f 547/743/708 532/747/711 563/748/712 +f 511/742/707 532/747/711 547/743/708 +f 545/744/709 563/748/712 560/749/713 +f 547/743/708 563/748/712 545/744/709 +f 518/745/679 560/749/713 535/750/679 +f 545/744/709 560/749/713 518/745/679 +f 440/746/710 400/701/671 433/751/714 +f 532/747/711 433/751/714 512/752/715 +f 440/746/710 433/751/714 532/747/711 +f 563/748/712 512/752/715 548/753/716 +f 532/747/711 512/752/715 563/748/712 +f 560/749/713 548/753/716 546/754/717 +f 563/748/712 548/753/716 560/749/713 +f 535/750/679 546/754/717 519/755/679 +f 560/749/713 546/754/717 535/750/679 +f 433/751/714 400/701/671 418/756/718 +f 512/752/715 418/756/718 456/757/719 +f 433/751/714 418/756/718 512/752/715 +f 548/753/716 456/757/719 476/758/720 +f 512/752/715 456/757/719 548/753/716 +f 546/754/717 476/758/720 470/759/721 +f 548/753/716 476/758/720 546/754/717 +f 519/755/679 470/759/721 453/760/679 +f 546/754/717 470/759/721 519/755/679 +f 418/756/718 400/701/671 387/700/670 +f 456/757/719 387/700/670 375/703/673 +f 418/756/718 387/700/670 456/757/719 +f 476/758/720 375/703/673 368/705/675 +f 456/757/719 375/703/673 476/758/720 +f 470/759/721 368/705/675 364/707/677 +f 476/758/720 368/705/675 470/759/721 +f 453/760/679 364/707/677 366/709/679 +f 470/759/721 364/707/677 453/760/679 +f 742/761/722 676/762/723 737/763/724 +f 851/764/725 737/763/724 839/765/726 +f 742/761/722 737/763/724 851/764/725 +f 902/766/727 839/765/726 886/767/728 +f 851/764/725 839/765/726 902/766/727 +f 901/768/729 886/767/728 884/769/730 +f 902/766/727 886/767/728 901/768/729 +f 864/770/731 884/769/730 858/771/732 +f 901/768/729 884/769/730 864/770/731 +f 737/763/724 676/762/723 729/772/733 +f 839/765/726 729/772/733 810/773/734 +f 737/763/724 729/772/733 839/765/726 +f 886/767/728 810/773/734 844/774/735 +f 839/765/726 810/773/734 886/767/728 +f 884/769/730 844/774/735 842/775/736 +f 886/767/728 844/774/735 884/769/730 +f 858/771/732 842/775/736 818/776/731 +f 884/769/730 842/775/736 858/771/732 +f 729/772/733 676/762/723 711/777/737 +f 810/773/734 711/777/737 762/778/738 +f 729/772/733 711/777/737 810/773/734 +f 844/774/735 762/778/738 787/779/739 +f 810/773/734 762/778/738 844/774/735 +f 842/775/736 787/779/739 785/780/740 +f 844/774/735 787/779/739 842/775/736 +f 818/776/731 785/780/740 771/781/731 +f 842/775/736 785/780/740 818/776/731 +f 711/777/737 676/762/723 679/782/741 +f 762/778/738 679/782/741 683/783/742 +f 711/777/737 679/782/741 762/778/738 +f 787/779/739 683/783/742 687/784/743 +f 762/778/738 683/783/742 787/779/739 +f 785/780/740 687/784/743 689/785/744 +f 787/779/739 687/784/743 785/780/740 +f 771/781/731 689/785/744 688/786/731 +f 785/780/740 689/785/744 771/781/731 +f 679/782/741 676/762/723 640/787/745 +f 683/783/742 640/787/745 590/788/746 +f 679/782/741 640/787/745 683/783/742 +f 687/784/743 590/788/746 569/789/747 +f 683/783/742 590/788/746 687/784/743 +f 689/785/744 569/789/747 571/790/748 +f 687/784/743 569/789/747 689/785/744 +f 688/786/731 571/790/748 582/791/749 +f 689/785/744 571/790/748 688/786/731 +f 640/787/745 676/762/723 635/792/750 +f 590/788/746 635/792/750 550/793/751 +f 640/787/745 635/792/750 590/788/746 +f 569/789/747 550/793/751 514/794/752 +f 590/788/746 550/793/751 569/789/747 +f 571/790/748 514/794/752 516/795/753 +f 569/789/747 514/794/752 571/790/748 +f 582/791/749 516/795/753 539/796/731 +f 571/790/748 516/795/753 582/791/749 +f 635/792/750 676/762/723 614/797/754 +f 550/793/751 614/797/754 521/798/755 +f 635/792/750 614/797/754 550/793/751 +f 514/794/752 521/798/755 468/799/756 +f 550/793/751 521/798/755 514/794/752 +f 516/795/753 468/799/756 472/800/757 +f 514/794/752 468/799/756 516/795/753 +f 539/796/731 472/800/757 497/801/731 +f 516/795/753 472/800/757 539/796/731 +f 614/797/754 676/762/723 608/802/758 +f 521/798/755 608/802/758 503/803/759 +f 614/797/754 608/802/758 521/798/755 +f 468/799/756 503/803/759 451/804/760 +f 521/798/755 503/803/759 468/799/756 +f 472/800/757 451/804/760 454/805/761 +f 468/799/756 451/804/760 472/800/757 +f 497/801/731 454/805/761 488/806/731 +f 472/800/757 454/805/761 497/801/731 +f 608/802/758 676/762/723 613/807/762 +f 503/803/759 613/807/762 520/808/763 +f 608/802/758 613/807/762 503/803/759 +f 451/804/760 520/808/763 467/809/764 +f 503/803/759 520/808/763 451/804/760 +f 454/805/761 467/809/764 471/810/765 +f 451/804/760 467/809/764 454/805/761 +f 488/806/731 471/810/765 496/811/731 +f 454/805/761 471/810/765 488/806/731 +f 613/807/762 676/762/723 634/812/766 +f 520/808/763 634/812/766 549/813/767 +f 613/807/762 634/812/766 520/808/763 +f 467/809/764 549/813/767 513/814/768 +f 520/808/763 549/813/767 467/809/764 +f 471/810/765 513/814/768 515/815/769 +f 467/809/764 513/814/768 471/810/765 +f 496/811/731 515/815/769 538/816/770 +f 471/810/765 515/815/769 496/811/731 +f 634/812/766 676/762/723 639/817/771 +f 549/813/767 639/817/771 589/818/772 +f 634/812/766 639/817/771 549/813/767 +f 513/814/768 589/818/772 568/819/773 +f 549/813/767 589/818/772 513/814/768 +f 515/815/769 568/819/773 570/820/774 +f 513/814/768 568/819/773 515/815/769 +f 538/816/770 570/820/774 581/821/749 +f 515/815/769 570/820/774 538/816/770 +f 639/817/771 676/762/723 675/822/775 +f 589/818/772 675/822/775 682/823/776 +f 639/817/771 675/822/775 589/818/772 +f 568/819/773 682/823/776 684/824/777 +f 589/818/772 682/823/776 568/819/773 +f 570/820/774 684/824/777 685/825/778 +f 568/819/773 684/824/777 570/820/774 +f 581/821/749 685/825/778 686/826/749 +f 570/820/774 685/825/778 581/821/749 +f 675/822/775 676/762/723 710/827/779 +f 682/823/776 710/827/779 761/828/780 +f 675/822/775 710/827/779 682/823/776 +f 684/824/777 761/828/780 786/829/781 +f 682/823/776 761/828/780 684/824/777 +f 685/825/778 786/829/781 784/830/782 +f 684/824/777 786/829/781 685/825/778 +f 686/826/749 784/830/782 770/831/749 +f 685/825/778 784/830/782 686/826/749 +f 710/827/779 676/762/723 728/832/783 +f 761/828/780 728/832/783 809/833/784 +f 710/827/779 728/832/783 761/828/780 +f 786/829/781 809/833/784 843/834/785 +f 761/828/780 809/833/784 786/829/781 +f 784/830/782 843/834/785 841/835/786 +f 786/829/781 843/834/785 784/830/782 +f 770/831/749 841/835/786 817/836/749 +f 784/830/782 841/835/786 770/831/749 +f 728/832/783 676/762/723 736/837/787 +f 809/833/784 736/837/787 838/838/788 +f 728/832/783 736/837/787 809/833/784 +f 843/834/785 838/838/788 885/839/789 +f 809/833/784 838/838/788 843/834/785 +f 841/835/786 885/839/789 883/840/790 +f 843/834/785 885/839/789 841/835/786 +f 817/836/749 883/840/790 857/841/731 +f 841/835/786 883/840/790 817/836/749 +f 736/837/787 676/762/723 742/761/722 +f 838/838/788 742/761/722 851/764/725 +f 736/837/787 742/761/722 838/838/788 +f 885/839/789 851/764/725 902/766/727 +f 838/838/788 851/764/725 885/839/789 +f 883/840/790 902/766/727 901/768/729 +f 885/839/789 902/766/727 883/840/790 +f 857/841/731 901/768/729 864/770/731 +f 883/840/790 901/768/729 857/841/731 +f 750/842/791 680/843/792 740/844/793 +f 802/845/794 740/844/793 792/846/795 +f 750/842/791 740/844/793 802/845/794 +f 845/847/796 792/846/795 826/848/797 +f 802/845/794 792/846/795 845/847/796 +f 873/849/798 826/848/797 855/850/799 +f 845/847/796 826/848/797 873/849/798 +f 905/851/800 855/850/799 879/852/801 +f 873/849/798 855/850/799 905/851/800 +f 918/853/802 879/852/801 899/854/803 +f 905/851/800 879/852/801 918/853/802 +f 921/855/804 899/854/803 903/856/805 +f 918/853/802 899/854/803 921/855/804 +f 740/844/793 680/843/792 723/857/806 +f 792/846/795 723/857/806 747/858/807 +f 740/844/793 723/857/806 792/846/795 +f 826/848/797 747/858/807 781/859/808 +f 792/846/795 747/858/807 826/848/797 +f 855/850/799 781/859/808 796/860/809 +f 826/848/797 781/859/808 855/850/799 +f 879/852/801 796/860/809 805/861/810 +f 855/850/799 796/860/809 879/852/801 +f 899/854/803 805/861/810 812/862/811 +f 879/852/801 805/861/810 899/854/803 +f 903/856/805 812/862/811 820/863/812 +f 899/854/803 812/862/811 903/856/805 +f 723/857/806 680/843/792 677/864/813 +f 747/858/807 677/864/813 674/865/814 +f 723/857/806 677/864/813 747/858/807 +f 781/859/808 674/865/814 670/866/815 +f 747/858/807 674/865/814 781/859/808 +f 796/860/809 670/866/815 667/867/816 +f 781/859/808 670/866/815 796/860/809 +f 805/861/810 667/867/816 666/868/817 +f 796/860/809 667/867/816 805/861/810 +f 812/862/811 666/868/817 664/869/818 +f 805/861/810 666/868/817 812/862/811 +f 820/863/812 664/869/818 663/870/819 +f 812/862/811 664/869/818 820/863/812 +f 677/864/813 680/843/792 636/871/820 +f 674/865/814 636/871/820 602/872/821 +f 677/864/813 636/871/820 674/865/814 +f 670/866/815 602/872/821 572/873/822 +f 674/865/814 602/872/821 670/866/815 +f 667/867/816 572/873/822 556/874/823 +f 670/866/815 572/873/822 667/867/816 +f 666/868/817 556/874/823 551/875/824 +f 667/867/816 556/874/823 666/868/817 +f 664/869/818 551/875/824 543/876/825 +f 666/868/817 551/875/824 664/869/818 +f 663/870/819 543/876/825 536/877/826 +f 664/869/818 543/876/825 663/870/819 +f 636/871/820 680/843/792 609/878/827 +f 602/872/821 609/878/827 561/879/828 +f 636/871/820 609/878/827 602/872/821 +f 572/873/822 561/879/828 527/880/829 +f 602/872/821 561/879/828 572/873/822 +f 556/874/823 527/880/829 498/881/830 +f 572/873/822 527/880/829 556/874/823 +f 551/875/824 498/881/830 478/882/831 +f 556/874/823 498/881/830 551/875/824 +f 543/876/825 478/882/831 457/883/832 +f 551/875/824 478/882/831 543/876/825 +f 536/877/826 457/883/832 449/884/833 +f 543/876/825 457/883/832 536/877/826 +f 609/878/827 680/843/792 600/885/834 +f 561/879/828 600/885/834 553/886/835 +f 609/878/827 600/885/834 561/879/828 +f 527/880/829 553/886/835 509/887/836 +f 561/879/828 553/886/835 527/880/829 +f 498/881/830 509/887/836 482/888/837 +f 527/880/829 509/887/836 498/881/830 +f 478/882/831 482/888/837 447/889/838 +f 498/881/830 482/888/837 478/882/831 +f 457/883/832 447/889/838 434/890/839 +f 478/882/831 447/889/838 457/883/832 +f 449/884/833 434/890/839 430/891/840 +f 457/883/832 434/890/839 449/884/833 +f 600/885/834 680/843/792 610/892/841 +f 553/886/835 610/892/841 562/893/842 +f 600/885/834 610/892/841 553/886/835 +f 509/887/836 562/893/842 528/894/843 +f 553/886/835 562/893/842 509/887/836 +f 482/888/837 528/894/843 499/895/844 +f 509/887/836 528/894/843 482/888/837 +f 447/889/838 499/895/844 479/896/845 +f 482/888/837 499/895/844 447/889/838 +f 434/890/839 479/896/845 458/897/846 +f 447/889/838 479/896/845 434/890/839 +f 430/891/840 458/897/846 450/898/847 +f 434/890/839 458/897/846 430/891/840 +f 610/892/841 680/843/792 637/899/848 +f 562/893/842 637/899/848 603/900/849 +f 610/892/841 637/899/848 562/893/842 +f 528/894/843 603/900/849 573/901/850 +f 562/893/842 603/900/849 528/894/843 +f 499/895/844 573/901/850 557/902/851 +f 528/894/843 573/901/850 499/895/844 +f 479/896/845 557/902/851 552/903/852 +f 499/895/844 557/902/851 479/896/845 +f 458/897/846 552/903/852 544/904/853 +f 479/896/845 552/903/852 458/897/846 +f 450/898/847 544/904/853 537/905/854 +f 458/897/846 544/904/853 450/898/847 +f 637/899/848 680/843/792 681/906/855 +f 603/900/849 681/906/855 678/907/856 +f 637/899/848 681/906/855 603/900/849 +f 573/901/850 678/907/856 672/908/857 +f 603/900/849 678/907/856 573/901/850 +f 557/902/851 672/908/857 673/909/858 +f 573/901/850 672/908/857 557/902/851 +f 552/903/852 673/909/858 671/910/859 +f 557/902/851 673/909/858 552/903/852 +f 544/904/853 671/910/859 669/911/860 +f 552/903/852 671/910/859 544/904/853 +f 537/905/854 669/911/860 665/912/861 +f 544/904/853 669/911/860 537/905/854 +f 681/906/855 680/843/792 724/913/862 +f 678/907/856 724/913/862 748/914/863 +f 681/906/855 724/913/862 678/907/856 +f 672/908/857 748/914/863 782/915/864 +f 678/907/856 748/914/863 672/908/857 +f 673/909/858 782/915/864 797/916/865 +f 672/908/857 782/915/864 673/909/858 +f 671/910/859 797/916/865 806/917/866 +f 673/909/858 797/916/865 671/910/859 +f 669/911/860 806/917/866 813/918/867 +f 671/910/859 806/917/866 669/911/860 +f 665/912/861 813/918/867 821/919/868 +f 669/911/860 813/918/867 665/912/861 +f 724/913/862 680/843/792 741/920/869 +f 748/914/863 741/920/869 793/921/870 +f 724/913/862 741/920/869 748/914/863 +f 782/915/864 793/921/870 827/922/871 +f 748/914/863 793/921/870 782/915/864 +f 797/916/865 827/922/871 856/923/872 +f 782/915/864 827/922/871 797/916/865 +f 806/917/866 856/923/872 880/924/873 +f 797/916/865 856/923/872 806/917/866 +f 813/918/867 880/924/873 900/925/874 +f 806/917/866 880/924/873 813/918/867 +f 821/919/868 900/925/874 904/926/875 +f 813/918/867 900/925/874 821/919/868 +f 741/920/869 680/843/792 750/842/791 +f 793/921/870 750/842/791 802/845/794 +f 741/920/869 750/842/791 793/921/870 +f 827/922/871 802/845/794 845/847/796 +f 793/921/870 802/845/794 827/922/871 +f 856/923/872 845/847/796 873/849/798 +f 827/922/871 845/847/796 856/923/872 +f 880/924/873 873/849/798 905/851/800 +f 856/923/872 873/849/798 880/924/873 +f 900/925/874 905/851/800 918/853/802 +f 880/924/873 905/851/800 900/925/874 +f 904/926/875 918/853/802 921/855/804 +f 900/925/874 918/853/802 904/926/875 +f 444/927/876 403/928/877 438/929/878 +f 484/930/879 438/929/878 473/931/880 +f 444/927/876 438/929/878 484/930/879 +f 534/932/881 473/931/880 522/933/882 +f 484/930/879 473/931/880 534/932/881 +f 555/934/883 522/933/882 541/935/884 +f 534/932/881 522/933/882 555/934/883 +f 588/936/885 541/935/884 558/937/886 +f 555/934/883 541/935/884 588/936/885 +f 628/938/887 558/937/886 574/939/888 +f 588/936/885 558/937/886 628/938/887 +f 631/940/889 574/939/888 583/941/890 +f 628/938/887 574/939/888 631/940/889 +f 438/929/878 403/928/877 423/942/891 +f 473/931/880 423/942/891 443/943/892 +f 438/929/878 423/942/891 473/931/880 +f 522/933/882 443/943/892 461/944/893 +f 473/931/880 443/943/892 522/933/882 +f 541/935/884 461/944/893 480/945/894 +f 522/933/882 461/944/893 541/935/884 +f 558/937/886 480/945/894 490/946/895 +f 541/935/884 480/945/894 558/937/886 +f 574/939/888 490/946/895 494/947/896 +f 558/937/886 490/946/895 574/939/888 +f 583/941/890 494/947/896 505/948/897 +f 574/939/888 494/947/896 583/941/890 +f 423/942/891 403/928/877 401/949/898 +f 443/943/892 401/949/898 397/950/899 +f 423/942/891 401/949/898 443/943/892 +f 461/944/893 397/950/899 394/951/900 +f 443/943/892 397/950/899 461/944/893 +f 480/945/894 394/951/900 393/952/901 +f 461/944/893 394/951/900 480/945/894 +f 490/946/895 393/952/901 392/953/902 +f 480/945/894 393/952/901 490/946/895 +f 494/947/896 392/953/902 390/954/903 +f 490/946/895 392/953/902 494/947/896 +f 505/948/897 390/954/903 389/955/904 +f 494/947/896 390/954/903 505/948/897 +f 401/949/898 403/928/877 356/956/905 +f 397/950/899 356/956/905 322/957/906 +f 401/949/898 356/956/905 397/950/899 +f 394/951/900 322/957/906 290/958/907 +f 397/950/899 322/957/906 394/951/900 +f 393/952/901 290/958/907 259/959/908 +f 394/951/900 290/958/907 393/952/901 +f 392/953/902 259/959/908 229/960/909 +f 393/952/901 259/959/908 392/953/902 +f 390/954/903 229/960/909 214/961/910 +f 392/953/902 229/960/909 390/954/903 +f 389/955/904 214/961/910 210/962/911 +f 390/954/903 214/961/910 389/955/904 +f 356/956/905 403/928/877 343/963/912 +f 322/957/906 343/963/912 266/964/913 +f 356/956/905 343/963/912 322/957/906 +f 290/958/907 266/964/913 194/965/914 +f 322/957/906 266/964/913 290/958/907 +f 259/959/908 194/965/914 161/966/915 +f 290/958/907 194/965/914 259/959/908 +f 229/960/909 161/966/915 134/967/916 +f 259/959/908 161/966/915 229/960/909 +f 214/961/910 134/967/916 126/968/917 +f 229/960/909 134/967/916 214/961/910 +f 210/962/911 126/968/917 121/969/918 +f 214/961/910 126/968/917 210/962/911 +f 343/963/912 403/928/877 318/970/919 +f 266/964/913 318/970/919 250/971/920 +f 343/963/912 318/970/919 266/964/913 +f 194/965/914 250/971/920 173/972/921 +f 266/964/913 250/971/920 194/965/914 +f 161/966/915 173/972/921 140/973/922 +f 194/965/914 173/972/921 161/966/915 +f 134/967/916 140/973/922 120/974/923 +f 161/966/915 140/973/922 134/967/916 +f 126/968/917 120/974/923 107/975/924 +f 134/967/916 120/974/923 126/968/917 +f 121/969/918 107/975/924 103/976/925 +f 126/968/917 107/975/924 121/969/918 +f 318/970/919 403/928/877 344/977/926 +f 250/971/920 344/977/926 267/978/927 +f 318/970/919 344/977/926 250/971/920 +f 173/972/921 267/978/927 195/979/928 +f 250/971/920 267/978/927 173/972/921 +f 140/973/922 195/979/928 162/980/929 +f 173/972/921 195/979/928 140/973/922 +f 120/974/923 162/980/929 135/981/930 +f 140/973/922 162/980/929 120/974/923 +f 107/975/924 135/981/930 127/982/931 +f 120/974/923 135/981/930 107/975/924 +f 103/976/925 127/982/931 122/983/932 +f 107/975/924 127/982/931 103/976/925 +f 344/977/926 403/928/877 357/984/933 +f 267/978/927 357/984/933 323/985/934 +f 344/977/926 357/984/933 267/978/927 +f 195/979/928 323/985/934 291/986/935 +f 267/978/927 323/985/934 195/979/928 +f 162/980/929 291/986/935 260/987/936 +f 195/979/928 291/986/935 162/980/929 +f 135/981/930 260/987/936 230/988/937 +f 162/980/929 260/987/936 135/981/930 +f 127/982/931 230/988/937 215/989/938 +f 135/981/930 230/988/937 127/982/931 +f 122/983/932 215/989/938 211/990/939 +f 127/982/931 215/989/938 122/983/932 +f 357/984/933 403/928/877 404/991/940 +f 323/985/934 404/991/940 402/992/941 +f 357/984/933 404/991/940 323/985/934 +f 291/986/935 402/992/941 398/993/942 +f 323/985/934 402/992/941 291/986/935 +f 260/987/936 398/993/942 399/994/943 +f 291/986/935 398/993/942 260/987/936 +f 230/988/937 399/994/943 396/995/944 +f 260/987/936 399/994/943 230/988/937 +f 215/989/938 396/995/944 395/996/945 +f 230/988/937 396/995/944 215/989/938 +f 211/990/939 395/996/945 391/997/946 +f 215/989/938 395/996/945 211/990/939 +f 404/991/940 403/928/877 424/998/947 +f 402/992/941 424/998/947 442/999/948 +f 404/991/940 424/998/947 402/992/941 +f 398/993/942 442/999/948 462/1000/949 +f 402/992/941 442/999/948 398/993/942 +f 399/994/943 462/1000/949 481/1001/950 +f 398/993/942 462/1000/949 399/994/943 +f 396/995/944 481/1001/950 491/1002/951 +f 399/994/943 481/1001/950 396/995/944 +f 395/996/945 491/1002/951 495/1003/952 +f 396/995/944 491/1002/951 395/996/945 +f 391/997/946 495/1003/952 506/1004/953 +f 395/996/945 495/1003/952 391/997/946 +f 424/998/947 403/928/877 439/1005/954 +f 442/999/948 439/1005/954 474/1006/955 +f 424/998/947 439/1005/954 442/999/948 +f 462/1000/949 474/1006/955 523/1007/956 +f 442/999/948 474/1006/955 462/1000/949 +f 481/1001/950 523/1007/956 542/1008/957 +f 462/1000/949 523/1007/956 481/1001/950 +f 491/1002/951 542/1008/957 559/1009/958 +f 481/1001/950 542/1008/957 491/1002/951 +f 495/1003/952 559/1009/958 575/1010/959 +f 491/1002/951 559/1009/958 495/1003/952 +f 506/1004/953 575/1010/959 584/1011/960 +f 495/1003/952 575/1010/959 506/1004/953 +f 439/1005/954 403/928/877 444/927/876 +f 474/1006/955 444/927/876 484/930/879 +f 439/1005/954 444/927/876 474/1006/955 +f 523/1007/956 484/930/879 534/932/881 +f 474/1006/955 484/930/879 523/1007/956 +f 542/1008/957 534/932/881 555/934/883 +f 523/1007/956 534/932/881 542/1008/957 +f 559/1009/958 555/934/883 588/936/885 +f 542/1008/957 555/934/883 559/1009/958 +f 575/1010/959 588/936/885 628/938/887 +f 559/1009/958 588/936/885 575/1010/959 +f 584/1011/960 628/938/887 631/940/889 +f 575/1010/959 628/938/887 584/1011/960 +f 1035/1012/961 966/1013/877 1008/1014/878 +f 1104/1015/879 1008/1014/878 1086/1016/880 +f 1035/1012/961 1008/1014/878 1104/1015/879 +f 1179/1017/962 1086/1016/880 1156/1018/882 +f 1104/1015/879 1086/1016/880 1179/1017/962 +f 1210/1019/883 1156/1018/882 1188/1020/884 +f 1179/1017/962 1156/1018/882 1210/1019/883 +f 1232/1021/885 1188/1020/884 1215/1022/886 +f 1210/1019/883 1188/1020/884 1232/1021/885 +f 1247/1023/887 1215/1022/886 1225/1024/888 +f 1232/1021/885 1215/1022/886 1247/1023/887 +f 1251/1025/889 1225/1024/888 1229/1026/963 +f 1247/1023/887 1225/1024/888 1251/1025/889 +f 1008/1014/878 966/1013/877 997/1027/964 +f 1086/1016/880 997/1027/964 1029/1028/892 +f 1008/1014/878 997/1027/964 1086/1016/880 +f 1156/1018/882 1029/1028/892 1064/1029/965 +f 1086/1016/880 1029/1028/892 1156/1018/882 +f 1188/1020/884 1064/1029/965 1092/1030/894 +f 1156/1018/882 1064/1029/965 1188/1020/884 +f 1215/1022/886 1092/1030/894 1122/1031/895 +f 1188/1020/884 1092/1030/894 1215/1022/886 +f 1225/1024/888 1122/1031/895 1142/1032/896 +f 1215/1022/886 1122/1031/895 1225/1024/888 +f 1229/1026/963 1142/1032/896 1144/1033/897 +f 1225/1024/888 1142/1032/896 1229/1026/963 +f 997/1027/964 966/1013/877 964/1034/966 +f 1029/1028/892 964/1034/966 960/1035/899 +f 997/1027/964 964/1034/966 1029/1028/892 +f 1064/1029/965 960/1035/899 957/1036/900 +f 1029/1028/892 960/1035/899 1064/1029/965 +f 1092/1030/894 957/1036/900 956/1037/901 +f 1064/1029/965 957/1036/900 1092/1030/894 +f 1122/1031/895 956/1037/901 955/1038/902 +f 1092/1030/894 956/1037/901 1122/1031/895 +f 1142/1032/896 955/1038/902 953/1039/903 +f 1122/1031/895 955/1038/902 1142/1032/896 +f 1144/1033/897 953/1039/903 952/1040/967 +f 1142/1032/896 953/1039/903 1144/1033/897 +f 964/1034/966 966/1013/877 926/1041/905 +f 960/1035/899 926/1041/905 910/1042/906 +f 964/1034/966 926/1041/905 960/1035/899 +f 957/1036/900 910/1042/906 890/1043/968 +f 960/1035/899 910/1042/906 957/1036/900 +f 956/1037/901 890/1043/968 877/1044/908 +f 957/1036/900 890/1043/968 956/1037/901 +f 955/1038/902 877/1044/908 861/1045/909 +f 956/1037/901 877/1044/908 955/1038/902 +f 953/1039/903 861/1045/909 859/1046/910 +f 955/1038/902 861/1045/909 953/1039/903 +f 952/1040/967 859/1046/910 848/1047/911 +f 953/1039/903 859/1046/910 952/1040/967 +f 926/1041/905 966/1013/877 913/1048/912 +f 910/1042/906 913/1048/912 881/1049/969 +f 926/1041/905 913/1048/912 910/1042/906 +f 890/1043/968 881/1049/969 836/1050/970 +f 910/1042/906 881/1049/969 890/1043/968 +f 877/1044/908 836/1050/970 814/1051/915 +f 890/1043/968 836/1050/970 877/1044/908 +f 861/1045/909 814/1051/915 794/1052/916 +f 877/1044/908 814/1051/915 861/1045/909 +f 859/1046/910 794/1052/916 779/1053/971 +f 861/1045/909 794/1052/916 859/1046/910 +f 848/1047/911 779/1053/971 768/1054/918 +f 859/1046/910 779/1053/971 848/1047/911 +f 913/1048/912 966/1013/877 909/1055/919 +f 881/1049/969 909/1055/919 869/1056/920 +f 913/1048/912 909/1055/919 881/1049/969 +f 836/1050/970 869/1056/920 822/1057/921 +f 881/1049/969 869/1056/920 836/1050/970 +f 814/1051/915 822/1057/921 798/1058/922 +f 836/1050/970 822/1057/921 814/1051/915 +f 794/1052/916 798/1058/922 763/1059/972 +f 814/1051/915 798/1058/922 794/1052/916 +f 779/1053/971 763/1059/972 735/1060/924 +f 794/1052/916 763/1059/972 779/1053/971 +f 768/1054/918 735/1060/924 732/1061/925 +f 779/1053/971 735/1060/924 768/1054/918 +f 909/1055/919 966/1013/877 914/1062/973 +f 869/1056/920 914/1062/973 882/1063/927 +f 909/1055/919 914/1062/973 869/1056/920 +f 822/1057/921 882/1063/927 837/1064/928 +f 869/1056/920 882/1063/927 822/1057/921 +f 798/1058/922 837/1064/928 815/1065/929 +f 822/1057/921 837/1064/928 798/1058/922 +f 763/1059/972 815/1065/929 795/1066/930 +f 798/1058/922 815/1065/929 763/1059/972 +f 735/1060/924 795/1066/930 780/1067/931 +f 763/1059/972 795/1066/930 735/1060/924 +f 732/1061/925 780/1067/931 769/1068/932 +f 735/1060/924 780/1067/931 732/1061/925 +f 914/1062/973 966/1013/877 927/1069/933 +f 882/1063/927 927/1069/933 911/1070/934 +f 914/1062/973 927/1069/933 882/1063/927 +f 837/1064/928 911/1070/934 891/1071/974 +f 882/1063/927 911/1070/934 837/1064/928 +f 815/1065/929 891/1071/974 878/1072/936 +f 837/1064/928 891/1071/974 815/1065/929 +f 795/1066/930 878/1072/936 862/1073/937 +f 815/1065/929 878/1072/936 795/1066/930 +f 780/1067/931 862/1073/937 860/1074/975 +f 795/1066/930 862/1073/937 780/1067/931 +f 769/1068/932 860/1074/975 849/1075/939 +f 780/1067/931 860/1074/975 769/1068/932 +f 927/1069/933 966/1013/877 967/1076/976 +f 911/1070/934 967/1076/976 965/1077/977 +f 927/1069/933 967/1076/976 911/1070/934 +f 891/1071/974 965/1077/977 961/1078/942 +f 911/1070/934 965/1077/977 891/1071/974 +f 878/1072/936 961/1078/942 962/1079/943 +f 891/1071/974 961/1078/942 878/1072/936 +f 862/1073/937 962/1079/943 959/1080/944 +f 878/1072/936 962/1079/943 862/1073/937 +f 860/1074/975 959/1080/944 958/1081/945 +f 862/1073/937 959/1080/944 860/1074/975 +f 849/1075/939 958/1081/945 954/1082/946 +f 860/1074/975 958/1081/945 849/1075/939 +f 967/1076/976 966/1013/877 998/1083/978 +f 965/1077/977 998/1083/978 1028/1084/948 +f 967/1076/976 998/1083/978 965/1077/977 +f 961/1078/942 1028/1084/948 1065/1085/949 +f 965/1077/977 1028/1084/948 961/1078/942 +f 962/1079/943 1065/1085/949 1093/1086/979 +f 961/1078/942 1065/1085/949 962/1079/943 +f 959/1080/944 1093/1086/979 1123/1087/951 +f 962/1079/943 1093/1086/979 959/1080/944 +f 958/1081/945 1123/1087/951 1143/1088/952 +f 959/1080/944 1123/1087/951 958/1081/945 +f 954/1082/946 1143/1088/952 1145/1089/953 +f 958/1081/945 1143/1088/952 954/1082/946 +f 998/1083/978 966/1013/877 1009/1090/954 +f 1028/1084/948 1009/1090/954 1087/1091/955 +f 998/1083/978 1009/1090/954 1028/1084/948 +f 1065/1085/949 1087/1091/955 1157/1092/980 +f 1028/1084/948 1087/1091/955 1065/1085/949 +f 1093/1086/979 1157/1092/980 1189/1093/957 +f 1065/1085/949 1157/1092/980 1093/1086/979 +f 1123/1087/951 1189/1093/957 1216/1094/958 +f 1093/1086/979 1189/1093/957 1123/1087/951 +f 1143/1088/952 1216/1094/958 1224/1095/959 +f 1123/1087/951 1216/1094/958 1143/1088/952 +f 1145/1089/953 1224/1095/959 1230/1096/981 +f 1143/1088/952 1224/1095/959 1145/1089/953 +f 1009/1090/954 966/1013/877 1035/1012/961 +f 1087/1091/955 1035/1012/961 1104/1015/879 +f 1009/1090/954 1035/1012/961 1087/1091/955 +f 1157/1092/980 1104/1015/879 1179/1017/962 +f 1087/1091/955 1104/1015/879 1157/1092/980 +f 1189/1093/957 1179/1017/962 1210/1019/883 +f 1157/1092/980 1179/1017/962 1189/1093/957 +f 1216/1094/958 1210/1019/883 1232/1021/885 +f 1189/1093/957 1210/1019/883 1216/1094/958 +f 1224/1095/959 1232/1021/885 1247/1023/887 +f 1216/1094/958 1232/1021/885 1224/1095/959 +f 1230/1096/981 1247/1023/887 1251/1025/889 +f 1224/1095/959 1247/1023/887 1230/1096/981 +f 1096/1097/982 1079/1098/983 1090/1099/984 +f 1136/1100/985 1090/1099/984 1100/1101/986 +f 1096/1097/982 1090/1099/984 1136/1100/985 +f 1152/1102/987 1100/1101/986 1119/1103/988 +f 1136/1100/985 1100/1101/986 1152/1102/987 +f 1168/1104/989 1119/1103/988 1134/1105/990 +f 1152/1102/987 1119/1103/988 1168/1104/989 +f 1090/1099/984 1079/1098/983 1078/1106/991 +f 1100/1101/986 1078/1106/991 1076/1107/992 +f 1090/1099/984 1078/1106/991 1100/1101/986 +f 1119/1103/988 1076/1107/992 1073/1108/993 +f 1100/1101/986 1076/1107/992 1119/1103/988 +f 1134/1105/990 1073/1108/993 1071/1109/994 +f 1119/1103/988 1073/1108/993 1134/1105/990 +f 1078/1106/991 1079/1098/983 1059/1110/995 +f 1076/1107/992 1059/1110/995 1050/1111/996 +f 1078/1106/991 1059/1110/995 1076/1107/992 +f 1073/1108/993 1050/1111/996 1037/1112/997 +f 1076/1107/992 1050/1111/996 1073/1108/993 +f 1071/1109/994 1037/1112/997 1026/1113/998 +f 1073/1108/993 1037/1112/997 1071/1109/994 +f 1059/1110/995 1079/1098/983 1054/1114/999 +f 1050/1111/996 1054/1114/999 1022/1115/1000 +f 1059/1110/995 1054/1114/999 1050/1111/996 +f 1037/1112/997 1022/1115/1000 1000/1116/1001 +f 1050/1111/996 1022/1115/1000 1037/1112/997 +f 1026/1113/998 1000/1116/1001 994/1117/1002 +f 1037/1112/997 1000/1116/1001 1026/1113/998 +f 1054/1114/999 1079/1098/983 1048/1118/1003 +f 1022/1115/1000 1048/1118/1003 1004/1119/1004 +f 1054/1114/999 1048/1118/1003 1022/1115/1000 +f 1000/1116/1001 1004/1119/1004 990/1120/1005 +f 1022/1115/1000 1004/1119/1004 1000/1116/1001 +f 994/1117/1002 990/1120/1005 971/1121/1006 +f 1000/1116/1001 990/1120/1005 994/1117/1002 +f 1048/1118/1003 1079/1098/983 1042/1122/1007 +f 1004/1119/1004 1042/1122/1007 1001/1123/1008 +f 1048/1118/1003 1042/1122/1007 1004/1119/1004 +f 990/1120/1005 1001/1123/1008 981/1124/1009 +f 1004/1119/1004 1001/1123/1008 990/1120/1005 +f 971/1121/1006 981/1124/1009 948/1125/1010 +f 990/1120/1005 981/1124/1009 971/1121/1006 +f 1042/1122/1007 1079/1098/983 1047/1126/1011 +f 1001/1123/1008 1047/1126/1011 1003/1127/1012 +f 1042/1122/1007 1047/1126/1011 1001/1123/1008 +f 981/1124/1009 1003/1127/1012 989/1128/1013 +f 1001/1123/1008 1003/1127/1012 981/1124/1009 +f 948/1125/1010 989/1128/1013 970/1129/1014 +f 981/1124/1009 989/1128/1013 948/1125/1010 +f 1047/1126/1011 1079/1098/983 1053/1130/1015 +f 1003/1127/1012 1053/1130/1015 1021/1131/1016 +f 1047/1126/1011 1053/1130/1015 1003/1127/1012 +f 989/1128/1013 1021/1131/1016 999/1132/1017 +f 1003/1127/1012 1021/1131/1016 989/1128/1013 +f 970/1129/1014 999/1132/1017 993/1133/1018 +f 989/1128/1013 999/1132/1017 970/1129/1014 +f 1053/1130/1015 1079/1098/983 1058/1134/1019 +f 1021/1131/1016 1058/1134/1019 1049/1135/1020 +f 1053/1130/1015 1058/1134/1019 1021/1131/1016 +f 999/1132/1017 1049/1135/1020 1036/1136/1021 +f 1021/1131/1016 1049/1135/1020 999/1132/1017 +f 993/1133/1018 1036/1136/1021 1025/1137/1022 +f 999/1132/1017 1036/1136/1021 993/1133/1018 +f 1058/1134/1019 1079/1098/983 1077/1138/1023 +f 1049/1135/1020 1077/1138/1023 1075/1139/1024 +f 1058/1134/1019 1077/1138/1023 1049/1135/1020 +f 1036/1136/1021 1075/1139/1024 1072/1140/1025 +f 1049/1135/1020 1075/1139/1024 1036/1136/1021 +f 1025/1137/1022 1072/1140/1025 1070/1141/1026 +f 1036/1136/1021 1072/1140/1025 1025/1137/1022 +f 1077/1138/1023 1079/1098/983 1089/1142/1027 +f 1075/1139/1024 1089/1142/1027 1099/1143/1028 +f 1077/1138/1023 1089/1142/1027 1075/1139/1024 +f 1072/1140/1025 1099/1143/1028 1118/1144/1029 +f 1075/1139/1024 1099/1143/1028 1072/1140/1025 +f 1070/1141/1026 1118/1144/1029 1133/1145/1030 +f 1072/1140/1025 1118/1144/1029 1070/1141/1026 +f 1089/1142/1027 1079/1098/983 1095/1146/1031 +f 1099/1143/1028 1095/1146/1031 1135/1147/1032 +f 1089/1142/1027 1095/1146/1031 1099/1143/1028 +f 1118/1144/1029 1135/1147/1032 1151/1148/1033 +f 1099/1143/1028 1135/1147/1032 1118/1144/1029 +f 1133/1145/1030 1151/1148/1033 1167/1149/1034 +f 1118/1144/1029 1151/1148/1033 1133/1145/1030 +f 1095/1146/1031 1079/1098/983 1105/1150/1035 +f 1135/1147/1032 1105/1150/1035 1147/1151/1036 +f 1095/1146/1031 1105/1150/1035 1135/1147/1032 +f 1151/1148/1033 1147/1151/1036 1171/1152/1037 +f 1135/1147/1032 1147/1151/1036 1151/1148/1033 +f 1167/1149/1034 1171/1152/1037 1185/1153/1038 +f 1151/1148/1033 1171/1152/1037 1167/1149/1034 +f 1105/1150/1035 1079/1098/983 1113/1154/1039 +f 1147/1151/1036 1113/1154/1039 1150/1155/1040 +f 1105/1150/1035 1113/1154/1039 1147/1151/1036 +f 1171/1152/1037 1150/1155/1040 1175/1156/1041 +f 1147/1151/1036 1150/1155/1040 1171/1152/1037 +f 1185/1153/1038 1175/1156/1041 1194/1157/1042 +f 1171/1152/1037 1175/1156/1041 1185/1153/1038 +f 1113/1154/1039 1079/1098/983 1106/1158/1043 +f 1150/1155/1040 1106/1158/1043 1148/1159/1044 +f 1113/1154/1039 1106/1158/1043 1150/1155/1040 +f 1175/1156/1041 1148/1159/1044 1172/1160/1045 +f 1150/1155/1040 1148/1159/1044 1175/1156/1041 +f 1194/1157/1042 1172/1160/1045 1186/1161/1046 +f 1175/1156/1041 1172/1160/1045 1194/1157/1042 +f 1106/1158/1043 1079/1098/983 1096/1097/982 +f 1148/1159/1044 1096/1097/982 1136/1100/985 +f 1106/1158/1043 1096/1097/982 1148/1159/1044 +f 1172/1160/1045 1136/1100/985 1152/1102/987 +f 1148/1159/1044 1136/1100/985 1172/1160/1045 +f 1186/1161/1046 1152/1102/987 1168/1104/989 +f 1172/1160/1045 1152/1102/987 1186/1161/1046 +f 301/1162/1047 284/1163/1048 296/1164/984 +f 331/1165/985 296/1164/984 305/1166/1049 +f 301/1162/1047 296/1164/984 331/1165/985 +f 355/1167/1050 305/1166/1049 315/1168/988 +f 331/1165/985 305/1166/1049 355/1167/1050 +f 361/1169/989 315/1168/988 327/1170/1051 +f 355/1167/1050 315/1168/988 361/1169/989 +f 296/1164/984 284/1163/1048 283/1171/991 +f 305/1166/1049 283/1171/991 281/1172/992 +f 296/1164/984 283/1171/991 305/1166/1049 +f 315/1168/988 281/1172/992 278/1173/993 +f 305/1166/1049 281/1172/992 315/1168/988 +f 327/1170/1051 278/1173/993 276/1174/994 +f 315/1168/988 278/1173/993 327/1170/1051 +f 283/1171/991 284/1163/1048 263/1175/995 +f 281/1172/992 263/1175/995 253/1176/1052 +f 283/1171/991 263/1175/995 281/1172/992 +f 278/1173/993 253/1176/1052 231/1177/1053 +f 281/1172/992 253/1176/1052 278/1173/993 +f 276/1174/994 231/1177/1053 220/1178/998 +f 278/1173/993 231/1177/1053 276/1174/994 +f 263/1175/995 284/1163/1048 257/1179/999 +f 253/1176/1052 257/1179/999 218/1180/1000 +f 263/1175/995 257/1179/999 253/1176/1052 +f 231/1177/1053 218/1180/1000 199/1181/1054 +f 253/1176/1052 218/1180/1000 231/1177/1053 +f 220/1178/998 199/1181/1054 186/1182/1055 +f 231/1177/1053 199/1181/1054 220/1178/998 +f 257/1179/999 284/1163/1048 249/1183/1003 +f 218/1180/1000 249/1183/1003 208/1184/1056 +f 257/1179/999 249/1183/1003 218/1180/1000 +f 199/1181/1054 208/1184/1056 178/1185/1005 +f 218/1180/1000 208/1184/1056 199/1181/1054 +f 186/1182/1055 178/1185/1005 166/1186/1006 +f 199/1181/1054 178/1185/1005 186/1182/1055 +f 249/1183/1003 284/1163/1048 241/1187/1057 +f 208/1184/1056 241/1187/1057 201/1188/1058 +f 249/1183/1003 241/1187/1057 208/1184/1056 +f 178/1185/1005 201/1188/1058 176/1189/1009 +f 208/1184/1056 201/1188/1058 178/1185/1005 +f 166/1186/1006 176/1189/1009 160/1190/1010 +f 178/1185/1005 176/1189/1009 166/1186/1006 +f 241/1187/1057 284/1163/1048 248/1191/1011 +f 201/1188/1058 248/1191/1011 207/1192/1012 +f 241/1187/1057 248/1191/1011 201/1188/1058 +f 176/1189/1009 207/1192/1012 177/1193/1059 +f 201/1188/1058 207/1192/1012 176/1189/1009 +f 160/1190/1010 177/1193/1059 165/1194/1014 +f 176/1189/1009 177/1193/1059 160/1190/1010 +f 248/1191/1011 284/1163/1048 256/1195/1015 +f 207/1192/1012 256/1195/1015 217/1196/1016 +f 248/1191/1011 256/1195/1015 207/1192/1012 +f 177/1193/1059 217/1196/1016 200/1197/1060 +f 207/1192/1012 217/1196/1016 177/1193/1059 +f 165/1194/1014 200/1197/1060 185/1198/1018 +f 177/1193/1059 200/1197/1060 165/1194/1014 +f 256/1195/1015 284/1163/1048 262/1199/1019 +f 217/1196/1016 262/1199/1019 252/1200/1020 +f 256/1195/1015 262/1199/1019 217/1196/1016 +f 200/1197/1060 252/1200/1020 232/1201/1061 +f 217/1196/1016 252/1200/1020 200/1197/1060 +f 185/1198/1018 232/1201/1061 219/1202/1022 +f 200/1197/1060 232/1201/1061 185/1198/1018 +f 262/1199/1019 284/1163/1048 282/1203/1023 +f 252/1200/1020 282/1203/1023 280/1204/1024 +f 262/1199/1019 282/1203/1023 252/1200/1020 +f 232/1201/1061 280/1204/1024 277/1205/1062 +f 252/1200/1020 280/1204/1024 232/1201/1061 +f 219/1202/1022 277/1205/1062 275/1206/1063 +f 232/1201/1061 277/1205/1062 219/1202/1022 +f 282/1203/1023 284/1163/1048 295/1207/1027 +f 280/1204/1024 295/1207/1027 304/1208/1064 +f 282/1203/1023 295/1207/1027 280/1204/1024 +f 277/1205/1062 304/1208/1064 314/1209/1029 +f 280/1204/1024 304/1208/1064 277/1205/1062 +f 275/1206/1063 314/1209/1029 326/1210/1065 +f 277/1205/1062 314/1209/1029 275/1206/1063 +f 295/1207/1027 284/1163/1048 300/1211/1066 +f 304/1208/1064 300/1211/1066 330/1212/1032 +f 295/1207/1027 300/1211/1066 304/1208/1064 +f 314/1209/1029 330/1212/1032 354/1213/1033 +f 304/1208/1064 330/1212/1032 314/1209/1029 +f 326/1210/1065 354/1213/1033 360/1214/1034 +f 314/1209/1029 354/1213/1033 326/1210/1065 +f 300/1211/1066 284/1163/1048 306/1215/1035 +f 330/1212/1032 306/1215/1035 347/1216/1067 +f 300/1211/1066 306/1215/1035 330/1212/1032 +f 354/1213/1033 347/1216/1067 370/1217/1068 +f 330/1212/1032 347/1216/1067 354/1213/1033 +f 360/1214/1034 370/1217/1068 382/1218/1038 +f 354/1213/1033 370/1217/1068 360/1214/1034 +f 306/1215/1035 284/1163/1048 311/1219/1039 +f 347/1216/1067 311/1219/1039 351/1220/1069 +f 306/1215/1035 311/1219/1039 347/1216/1067 +f 370/1217/1068 351/1220/1069 373/1221/1070 +f 347/1216/1067 351/1220/1069 370/1217/1068 +f 382/1218/1038 373/1221/1070 406/1222/1071 +f 370/1217/1068 373/1221/1070 382/1218/1038 +f 311/1219/1039 284/1163/1048 307/1223/1072 +f 351/1220/1069 307/1223/1072 348/1224/1073 +f 311/1219/1039 307/1223/1072 351/1220/1069 +f 373/1221/1070 348/1224/1073 371/1225/1074 +f 351/1220/1069 348/1224/1073 373/1221/1070 +f 406/1222/1071 371/1225/1074 383/1226/1075 +f 373/1221/1070 371/1225/1074 406/1222/1071 +f 307/1223/1072 284/1163/1048 301/1162/1047 +f 348/1224/1073 301/1162/1047 331/1165/985 +f 307/1223/1072 301/1162/1047 348/1224/1073 +f 371/1225/1074 331/1165/985 355/1167/1050 +f 348/1224/1073 331/1165/985 371/1225/1074 +f 383/1226/1075 355/1167/1050 361/1169/989 +f 371/1225/1074 355/1167/1050 383/1226/1075 +f 1080/1227/1076 1250/1228/1077 1249/580/561 +f 1081/1229/1076 1250/1228/1077 1080/1227/1076 +f 1249/580/561 1279/1230/1078 1278/592/573 +f 1250/1228/1077 1279/1230/1078 1249/580/561 +f 1278/592/573 1286/1231/1079 1285/598/579 +f 1279/1230/1078 1286/1231/1079 1278/592/573 +f 1285/598/579 1290/1232/1080 1289/1233/1080 +f 1286/1231/1079 1290/1232/1080 1285/598/579 +f 1289/1233/1080 1288/1234/1081 1287/1235/1081 +f 1290/1232/1080 1288/1234/1081 1289/1233/1080 +f 1287/1235/1081 1283/1236/1082 1282/1237/1082 +f 1288/1234/1081 1283/1236/1082 1287/1235/1081 +f 1282/1237/1082 1281/1238/1083 1280/604/585 +f 1283/1236/1082 1281/1238/1083 1282/1237/1082 +f 1280/604/585 1242/1239/1084 1241/1240/1084 +f 1281/1238/1083 1242/1239/1084 1280/604/585 +f 1241/1240/1084 1081/1229/1076 1080/1227/1076 +f 1242/1239/1084 1081/1229/1076 1241/1240/1084 +f 273/1241/1085 104/521/502 105/1242/1086 +f 274/1243/1085 273/1241/1085 105/1242/1086 +f 104/521/502 75/532/513 76/1244/1087 +f 105/1242/1086 104/521/502 76/1244/1087 +f 75/532/513 66/538/519 67/1245/1088 +f 76/1244/1087 75/532/513 67/1245/1088 +f 66/538/519 62/1246/1089 63/1247/1089 +f 67/1245/1088 66/538/519 63/1247/1089 +f 62/1246/1089 64/1248/1090 65/1249/1090 +f 63/1247/1089 62/1246/1089 65/1249/1090 +f 64/1248/1090 68/1250/1091 69/1251/1091 +f 65/1249/1090 64/1248/1090 69/1251/1091 +f 68/1250/1091 72/544/525 73/1252/1092 +f 69/1251/1091 68/1250/1091 73/1252/1092 +f 72/544/525 113/1253/1093 114/1254/1093 +f 73/1252/1092 72/544/525 114/1254/1093 +f 113/1253/1093 273/1241/1085 274/1243/1085 +f 114/1254/1093 113/1253/1093 274/1243/1085 +f 1097/1255/1094 1074/1256/1095 1084/1257/1096 +f 1184/1258/1097 1084/1257/1096 1121/1259/1098 +f 1097/1255/1094 1084/1257/1096 1184/1258/1097 +f 1181/1260/1099 1121/1259/1098 1120/1261/1100 +f 1184/1258/1097 1121/1259/1098 1181/1260/1099 +f 1170/1262/1048 1120/1261/1100 1110/1263/1048 +f 1181/1260/1099 1120/1261/1100 1170/1262/1048 +f 1084/1257/1096 1074/1256/1095 1060/1264/1101 +f 1121/1259/1098 1060/1264/1101 1032/1265/1102 +f 1084/1257/1096 1060/1264/1101 1121/1259/1098 +f 1120/1261/1100 1032/1265/1102 1034/1266/1103 +f 1121/1259/1098 1032/1265/1102 1120/1261/1100 +f 1110/1263/1048 1034/1266/1103 1041/1267/1048 +f 1120/1261/1100 1034/1266/1103 1110/1263/1048 +f 1060/1264/1101 1074/1256/1095 1051/1268/1104 +f 1032/1265/1102 1051/1268/1104 969/1269/1105 +f 1060/1264/1101 1051/1268/1104 1032/1265/1102 +f 1034/1266/1103 969/1269/1105 974/1270/1106 +f 1032/1265/1102 969/1269/1105 1034/1266/1103 +f 1041/1267/1048 974/1270/1106 991/1271/1048 +f 1034/1266/1103 974/1270/1106 1041/1267/1048 +f 1051/1268/1104 1074/1256/1095 1043/1272/1107 +f 969/1269/1105 1043/1272/1107 925/1273/1108 +f 1051/1268/1104 1043/1272/1107 969/1269/1105 +f 974/1270/1106 925/1273/1108 929/1274/1109 +f 969/1269/1105 925/1273/1108 974/1270/1106 +f 991/1271/1048 929/1274/1109 943/1275/1048 +f 974/1270/1106 929/1274/1109 991/1271/1048 +f 1043/1272/1107 1074/1256/1095 1044/1276/1110 +f 925/1273/1108 1044/1276/1110 928/1277/1111 +f 1043/1272/1107 1044/1276/1110 925/1273/1108 +f 929/1274/1109 928/1277/1111 932/1278/1112 +f 925/1273/1108 928/1277/1111 929/1274/1109 +f 943/1275/1048 932/1278/1112 944/1279/983 +f 929/1274/1109 932/1278/1112 943/1275/1048 +f 1044/1276/1110 1074/1256/1095 1052/1280/1113 +f 928/1277/1111 1052/1280/1113 975/1281/1114 +f 1044/1276/1110 1052/1280/1113 928/1277/1111 +f 932/1278/1112 975/1281/1114 976/1282/1115 +f 928/1277/1111 975/1281/1114 932/1278/1112 +f 944/1279/983 976/1282/1115 992/1283/983 +f 932/1278/1112 976/1282/1115 944/1279/983 +f 1052/1280/1113 1074/1256/1095 1061/1284/1116 +f 975/1281/1114 1061/1284/1116 1039/1285/1117 +f 1052/1280/1113 1061/1284/1116 975/1281/1114 +f 976/1282/1115 1039/1285/1117 1040/1286/1118 +f 975/1281/1114 1039/1285/1117 976/1282/1115 +f 992/1283/983 1040/1286/1118 1046/1287/1048 +f 976/1282/1115 1040/1286/1118 992/1283/983 +f 1061/1284/1116 1074/1256/1095 1085/1288/1119 +f 1039/1285/1117 1085/1288/1119 1128/1289/1120 +f 1061/1284/1116 1085/1288/1119 1039/1285/1117 +f 1040/1286/1118 1128/1289/1120 1127/1290/1121 +f 1039/1285/1117 1128/1289/1120 1040/1286/1118 +f 1046/1287/1048 1127/1290/1121 1115/1291/1122 +f 1040/1286/1118 1127/1290/1121 1046/1287/1048 +f 1085/1288/1119 1074/1256/1095 1098/1292/1123 +f 1128/1289/1120 1098/1292/1123 1190/1293/1124 +f 1085/1288/1119 1098/1292/1123 1128/1289/1120 +f 1127/1290/1121 1190/1293/1124 1187/1294/1125 +f 1128/1289/1120 1190/1293/1124 1127/1290/1121 +f 1115/1291/1122 1187/1294/1125 1173/1295/1048 +f 1127/1290/1121 1187/1294/1125 1115/1291/1122 +f 1098/1292/1123 1074/1256/1095 1112/1296/1126 +f 1190/1293/1124 1112/1296/1126 1217/1297/1127 +f 1098/1292/1123 1112/1296/1126 1190/1293/1124 +f 1187/1294/1125 1217/1297/1127 1212/1298/1128 +f 1190/1293/1124 1217/1297/1127 1187/1294/1125 +f 1173/1295/1048 1212/1298/1128 1198/1299/1048 +f 1187/1294/1125 1212/1298/1128 1173/1295/1048 +f 1112/1296/1126 1074/1256/1095 1111/1300/1129 +f 1217/1297/1127 1111/1300/1129 1214/1301/1130 +f 1112/1296/1126 1111/1300/1129 1217/1297/1127 +f 1212/1298/1128 1214/1301/1130 1211/1302/1131 +f 1217/1297/1127 1214/1301/1130 1212/1298/1128 +f 1198/1299/1048 1211/1302/1131 1195/1303/1048 +f 1212/1298/1128 1211/1302/1131 1198/1299/1048 +f 1111/1300/1129 1074/1256/1095 1097/1255/1094 +f 1214/1301/1130 1097/1255/1094 1184/1258/1097 +f 1111/1300/1129 1097/1255/1094 1214/1301/1130 +f 1211/1302/1131 1184/1258/1097 1181/1260/1099 +f 1214/1301/1130 1184/1258/1097 1211/1302/1131 +f 1195/1303/1048 1181/1260/1099 1170/1262/1048 +f 1211/1302/1131 1181/1260/1099 1195/1303/1048 +f 302/1304/1132 279/1305/1095 293/1306/1133 +f 380/1307/1097 293/1306/1133 317/1308/1098 +f 302/1304/1132 293/1306/1133 380/1307/1097 +f 379/1309/1134 317/1308/1098 316/1310/1135 +f 380/1307/1097 317/1308/1098 379/1309/1134 +f 369/1311/1136 316/1310/1135 308/1312/1137 +f 379/1309/1134 316/1310/1135 369/1311/1136 +f 369/1311/1136 308/1312/1137 285/1313/1138 +f 293/1306/1133 279/1305/1095 269/1314/1139 +f 317/1308/1098 269/1314/1139 226/1315/1102 +f 293/1306/1133 269/1314/1139 317/1308/1098 +f 316/1310/1135 226/1315/1102 228/1316/1103 +f 317/1308/1098 226/1315/1102 316/1310/1135 +f 308/1312/1137 228/1316/1103 239/1317/1140 +f 316/1310/1135 228/1316/1103 308/1312/1137 +f 308/1312/1137 239/1317/1140 285/1313/1138 +f 269/1314/1139 279/1305/1095 254/1318/1104 +f 226/1315/1102 254/1318/1104 163/1319/1105 +f 269/1314/1139 254/1318/1104 226/1315/1102 +f 228/1316/1103 163/1319/1105 167/1320/1106 +f 226/1315/1102 163/1319/1105 228/1316/1103 +f 239/1317/1140 167/1320/1106 179/1321/1141 +f 228/1316/1103 167/1320/1106 239/1317/1140 +f 239/1317/1140 179/1321/1141 285/1313/1138 +f 254/1318/1104 279/1305/1095 242/1322/1107 +f 163/1319/1105 242/1322/1107 136/1323/1108 +f 254/1318/1104 242/1322/1107 163/1319/1105 +f 167/1320/1106 136/1323/1108 138/1324/1142 +f 163/1319/1105 136/1323/1108 167/1320/1106 +f 179/1321/1141 138/1324/1142 156/1325/1143 +f 167/1320/1106 138/1324/1142 179/1321/1141 +f 179/1321/1141 156/1325/1143 285/1313/1138 +f 242/1322/1107 279/1305/1095 243/1326/1144 +f 136/1323/1108 243/1326/1144 137/1327/1145 +f 242/1322/1107 243/1326/1144 136/1323/1108 +f 138/1324/1142 137/1327/1145 139/1328/1146 +f 136/1323/1108 137/1327/1145 138/1324/1142 +f 156/1325/1143 139/1328/1146 158/1329/1147 +f 138/1324/1142 139/1328/1146 156/1325/1143 +f 156/1325/1143 158/1329/1147 285/1313/1138 +f 243/1326/1144 279/1305/1095 255/1330/1148 +f 137/1327/1145 255/1330/1148 168/1331/1114 +f 243/1326/1144 255/1330/1148 137/1327/1145 +f 139/1328/1146 168/1331/1114 172/1332/1149 +f 137/1327/1145 168/1331/1114 139/1328/1146 +f 158/1329/1147 172/1332/1149 183/1333/1150 +f 139/1328/1146 172/1332/1149 158/1329/1147 +f 158/1329/1147 183/1333/1150 285/1313/1138 +f 255/1330/1148 279/1305/1095 272/1334/1151 +f 168/1331/1114 272/1334/1151 233/1335/1117 +f 255/1330/1148 272/1334/1151 168/1331/1114 +f 172/1332/1149 233/1335/1117 234/1336/1152 +f 168/1331/1114 233/1335/1117 172/1332/1149 +f 183/1333/1150 234/1336/1152 244/1337/1153 +f 172/1332/1149 234/1336/1152 183/1333/1150 +f 183/1333/1150 244/1337/1153 285/1313/1138 +f 272/1334/1151 279/1305/1095 294/1338/1154 +f 233/1335/1117 294/1338/1154 324/1339/1120 +f 272/1334/1151 294/1338/1154 233/1335/1117 +f 234/1336/1152 324/1339/1120 319/1340/1121 +f 233/1335/1117 324/1339/1120 234/1336/1152 +f 244/1337/1153 319/1340/1121 312/1341/1155 +f 234/1336/1152 319/1340/1121 244/1337/1153 +f 244/1337/1153 312/1341/1155 285/1313/1138 +f 294/1338/1154 279/1305/1095 303/1342/1156 +f 324/1339/1120 303/1342/1156 385/1343/1157 +f 294/1338/1154 303/1342/1156 324/1339/1120 +f 319/1340/1121 385/1343/1157 384/1344/1158 +f 324/1339/1120 385/1343/1157 319/1340/1121 +f 312/1341/1155 384/1344/1158 372/1345/1159 +f 319/1340/1121 384/1344/1158 312/1341/1155 +f 312/1341/1155 372/1345/1159 285/1313/1138 +f 303/1342/1156 279/1305/1095 310/1346/1126 +f 385/1343/1157 310/1346/1126 426/1347/1127 +f 303/1342/1156 310/1346/1126 385/1343/1157 +f 384/1344/1158 426/1347/1127 422/1348/1160 +f 385/1343/1157 426/1347/1127 384/1344/1158 +f 372/1345/1159 422/1348/1160 411/1349/1161 +f 384/1344/1158 422/1348/1160 372/1345/1159 +f 372/1345/1159 411/1349/1161 285/1313/1138 +f 310/1346/1126 279/1305/1095 309/1350/1129 +f 426/1347/1127 309/1350/1129 425/1351/1162 +f 310/1346/1126 309/1350/1129 426/1347/1127 +f 422/1348/1160 425/1351/1162 421/1352/1163 +f 426/1347/1127 425/1351/1162 422/1348/1160 +f 411/1349/1161 421/1352/1163 410/1353/1164 +f 422/1348/1160 421/1352/1163 411/1349/1161 +f 411/1349/1161 410/1353/1164 285/1313/1138 +f 309/1350/1129 279/1305/1095 302/1304/1132 +f 425/1351/1162 302/1304/1132 380/1307/1097 +f 309/1350/1129 302/1304/1132 425/1351/1162 +f 421/1352/1163 380/1307/1097 379/1309/1134 +f 425/1351/1162 380/1307/1097 421/1352/1163 +f 410/1353/1164 379/1309/1134 369/1311/1136 +f 421/1352/1163 379/1309/1134 410/1353/1164 +f 410/1353/1164 369/1311/1136 285/1313/1138 diff --git a/tests/barstest/shuttle.png b/tests/barstest/shuttle.png new file mode 100644 index 00000000..52d6244c Binary files /dev/null and b/tests/barstest/shuttle.png differ -- cgit v1.2.3 From 0c11550bed204807b8b366fd07862a5da9cd2251 Mon Sep 17 00:00:00 2001 From: Mika Salmela Date: Wed, 17 Sep 2014 12:35:04 +0300 Subject: Full update for static buffers when data updated MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Task-number: QTRD-3322 Change-Id: I6ac9e0506ed7355e06d2174d8f8f551cb82b8a86 Reviewed-by: Tomi Korpipää Reviewed-by: Miikka Heikkinen --- src/datavisualization/engine/scatter3drenderer.cpp | 9 ++++++++- src/datavisualization/engine/scatterseriesrendercache.cpp | 1 + src/datavisualization/engine/scatterseriesrendercache_p.h | 3 +++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/datavisualization/engine/scatter3drenderer.cpp b/src/datavisualization/engine/scatter3drenderer.cpp index 34386ca6..22d40a74 100644 --- a/src/datavisualization/engine/scatter3drenderer.cpp +++ b/src/datavisualization/engine/scatter3drenderer.cpp @@ -200,6 +200,10 @@ void Scatter3DRenderer::updateData() for (int i = 0; i < dataSize; i++) updateRenderItem(dataArray.at(i), renderArray[i]); + + if (m_cachedOptimizationHint.testFlag(QAbstract3DGraph::OptimizationStatic)) + cache->setStaticBufferDirty(true); + cache->setDataDirty(false); } } @@ -232,7 +236,8 @@ void Scatter3DRenderer::updateData() cache->setBufferObject(object); } if (renderArraySize != cache->oldArraySize() - || cache->object()->objectFile() != cache->oldMeshFileName()) { + || cache->object()->objectFile() != cache->oldMeshFileName() + || cache->staticBufferDirty()) { object->setScaleY(m_scaleY); object->fullLoad(cache, m_dotSizeScale); cache->setOldArraySize(renderArraySize); @@ -241,6 +246,8 @@ void Scatter3DRenderer::updateData() object->update(cache, m_dotSizeScale); } } + + cache->setStaticBufferDirty(false); } } } diff --git a/src/datavisualization/engine/scatterseriesrendercache.cpp b/src/datavisualization/engine/scatterseriesrendercache.cpp index e8888d19..4bc733ac 100644 --- a/src/datavisualization/engine/scatterseriesrendercache.cpp +++ b/src/datavisualization/engine/scatterseriesrendercache.cpp @@ -27,6 +27,7 @@ ScatterSeriesRenderCache::ScatterSeriesRenderCache(QAbstract3DSeries *series, : SeriesRenderCache(series, renderer), m_itemSize(0.0f), m_selectionIndexOffset(0), + m_staticBufferDirty(false), m_oldRenderArraySize(0), m_oldMeshFileName(QString()), m_scatterBufferObj(0), diff --git a/src/datavisualization/engine/scatterseriesrendercache_p.h b/src/datavisualization/engine/scatterseriesrendercache_p.h index 490e21fb..e7643748 100644 --- a/src/datavisualization/engine/scatterseriesrendercache_p.h +++ b/src/datavisualization/engine/scatterseriesrendercache_p.h @@ -53,6 +53,8 @@ public: inline float itemSize() const { return m_itemSize; } inline void setSelectionIndexOffset(int offset) { m_selectionIndexOffset = offset; } inline int selectionIndexOffset() const { return m_selectionIndexOffset; } + inline void setStaticBufferDirty(bool state) { m_staticBufferDirty = state; } + inline bool staticBufferDirty() const { return m_staticBufferDirty; } inline int oldArraySize() const { return m_oldRenderArraySize; } inline void setOldArraySize(int size) { m_oldRenderArraySize = size; } inline const QString &oldMeshFileName() const { return m_oldMeshFileName; } @@ -66,6 +68,7 @@ protected: ScatterRenderItemArray m_renderArray; float m_itemSize; int m_selectionIndexOffset; // Temporarily cached value for selection color calculations + bool m_staticBufferDirty; int m_oldRenderArraySize; // Used to detect if full buffer change needed QString m_oldMeshFileName; // Used to detect if full buffer change needed ScatterObjectBufferHelper *m_scatterBufferObj; -- cgit v1.2.3 From 98e0c04fe78da9735c2943524bd36648b2654f98 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Thu, 18 Sep 2014 13:49:23 +0300 Subject: Added possibility to change graph's locale MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Locale affects how axis labels are formatted. Default locale is still the "C". Task-number: QTRD-3229 Change-Id: I6126ce676906f4bbc91ae0abd18775bc1d564118 Reviewed-by: Mika Salmela Reviewed-by: Tomi Korpipää --- src/datavisualization/axis/qvalue3daxis.cpp | 24 +++++- .../axis/qvalue3daxisformatter.cpp | 40 +++++++++- src/datavisualization/axis/qvalue3daxisformatter.h | 4 + .../axis/qvalue3daxisformatter_p.h | 8 ++ src/datavisualization/data/qbar3dseries.cpp | 21 ++++-- ...tdatavisualization-qml-abstractdeclarative.qdoc | 10 +++ .../engine/abstract3dcontroller.cpp | 27 +++++++ .../engine/abstract3dcontroller_p.h | 6 ++ .../engine/abstract3drenderer_p.h | 2 + src/datavisualization/engine/qabstract3dgraph.cpp | 21 ++++++ src/datavisualization/engine/qabstract3dgraph.h | 6 ++ src/datavisualization/utils/utils.cpp | 85 ++++++++++++++-------- src/datavisualization/utils/utils_p.h | 10 ++- src/datavisualizationqml2/abstractdeclarative.cpp | 16 ++++ src/datavisualizationqml2/abstractdeclarative_p.h | 5 ++ 15 files changed, 238 insertions(+), 47 deletions(-) diff --git a/src/datavisualization/axis/qvalue3daxis.cpp b/src/datavisualization/axis/qvalue3daxis.cpp index 8207174f..3b9c9e3d 100644 --- a/src/datavisualization/axis/qvalue3daxis.cpp +++ b/src/datavisualization/axis/qvalue3daxis.cpp @@ -18,6 +18,7 @@ #include "qvalue3daxis_p.h" #include "qvalue3daxisformatter_p.h" +#include "abstract3dcontroller_p.h" QT_BEGIN_NAMESPACE_DATAVISUALIZATION @@ -68,8 +69,16 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION /*! * \qmlproperty string ValueAxis3D::labelFormat * - * Defines the label format to be used for the labels on this axis. Supported specifiers are: + * Defines the label format to be used for the labels on this axis. How the format is interpreted + * depends on the axis formatter and the locale in use. Using the default formatter and default + * locale (\c{"C"}), the formatting uses QString::sprintf(). Supported specifiers are: * \c {d, i, o, x, X, f, F, e, E, g, G, c}. See QString::sprintf() for additional details. + * For other locales, the default formatter uses reduced set of printf format specifiers: + * \c {d, i, f, F, e, E, g, G}. In these cases, the only supported modifier is the precision + * modifier for the floating point and exponential formats. The decimal point and other locale + * dependent formatting is done according to the graph locale. + * + * \sa AbstractGraph3D::locale */ /*! @@ -164,12 +173,20 @@ int QValue3DAxis::subSegmentCount() const /*! * \property QValue3DAxis::labelFormat * - * Defines the label \a format to be used for the labels on this axis. Supported specifiers are: + * Defines the label format to be used for the labels on this axis. How the format is interpreted + * depends on the axis formatter and the locale in use. Using the default formatter and default + * locale (\c{"C"}), the formatting uses QString::sprintf(). Supported specifiers are: * \c {d, i, o, x, X, f, F, e, E, g, G, c}. See QString::sprintf() for additional details. + * For other locales, the default formatter uses reduced set of printf format specifiers: + * \c {d, i, f, F, e, E, g, G}. In these cases, the only supported modifier is the precision + * modifier for the floating point and exponential formats. The decimal point and other locale + * dependent formatting is done according to the graph locale. * * Usage example: * * \c {axis->setLabelFormat("%.2f mm");} + * + * \sa formatter, QAbstract3DGraph::locale */ void QValue3DAxis::setLabelFormat(const QString &format) { @@ -201,6 +218,9 @@ void QValue3DAxis::setFormatter(QValue3DAxisFormatter *formatter) dptr()->m_formatter = formatter; formatter->setParent(this); formatter->d_ptr->setAxis(this); + Abstract3DController *controller = qobject_cast(parent()); + if (controller) + formatter->setLocale(controller->locale()); emit formatterChanged(formatter); emit dptr()->formatterDirty(); } diff --git a/src/datavisualization/axis/qvalue3daxisformatter.cpp b/src/datavisualization/axis/qvalue3daxisformatter.cpp index 56ca3b0f..f6b705a9 100644 --- a/src/datavisualization/axis/qvalue3daxisformatter.cpp +++ b/src/datavisualization/axis/qvalue3daxisformatter.cpp @@ -270,6 +270,28 @@ QStringList &QValue3DAxisFormatter::labelStrings() const return d_ptr->m_labelStrings; } +/*! + * Sets the \a locale that this formatter uses. + * The graph automatically sets the formatter's locale to a graph's locale whenever the parent axis + * is set as an active axis of the graph, the axis formatter is set to an axis attached to + * the graph, or the graph's locale changes. + * + * \sa locale(), QAbstract3DGraph::locale + */ +void QValue3DAxisFormatter::setLocale(const QLocale &locale) +{ + d_ptr->m_cLocaleInUse = (locale == QLocale::c()); + d_ptr->m_locale = locale; + markDirty(true); +} +/*! + * \return the current locale this formatter is using. + */ +QLocale QValue3DAxisFormatter::locale() const +{ + return d_ptr->m_locale; +} + // QValue3DAxisFormatterPrivate QValue3DAxisFormatterPrivate::QValue3DAxisFormatterPrivate(QValue3DAxisFormatter *q) : QObject(0), @@ -281,7 +303,10 @@ QValue3DAxisFormatterPrivate::QValue3DAxisFormatterPrivate(QValue3DAxisFormatter m_axis(0), m_preparsedParamType(Utils::ParamTypeUnknown), m_allowNegatives(true), - m_allowZero(true) + m_allowZero(true), + m_formatPrecision(6), // 6 and 'g' are defaults in Qt API for format precision and spec + m_formatSpec('g'), + m_cLocaleInUse(true) { } @@ -363,12 +388,19 @@ QString QValue3DAxisFormatterPrivate::stringForValue(qreal value, const QString { if (m_previousLabelFormat.compare(format)) { // Format string different than the previous one used, reparse it - m_previousLabelFormat = format; - m_preparsedParamType = Utils::findFormatParamType(format); m_labelFormatArray = format.toUtf8(); + m_previousLabelFormat = format; + m_preparsedParamType = Utils::preParseFormat(format, m_formatPreStr, m_formatPostStr, + m_formatPrecision, m_formatSpec); } - return Utils::formatLabel(m_labelFormatArray, m_preparsedParamType, value); + if (m_cLocaleInUse) { + return Utils::formatLabelSprintf(m_labelFormatArray, m_preparsedParamType, value); + } else { + return Utils::formatLabelLocalized(m_preparsedParamType, value, m_locale, m_formatPreStr, + m_formatPostStr, m_formatPrecision, m_formatSpec, + m_labelFormatArray); + } } float QValue3DAxisFormatterPrivate::positionAt(float value) const diff --git a/src/datavisualization/axis/qvalue3daxisformatter.h b/src/datavisualization/axis/qvalue3daxisformatter.h index c7b0bac5..82e49f21 100644 --- a/src/datavisualization/axis/qvalue3daxisformatter.h +++ b/src/datavisualization/axis/qvalue3daxisformatter.h @@ -24,6 +24,7 @@ #include #include #include +#include QT_BEGIN_NAMESPACE_DATAVISUALIZATION @@ -60,6 +61,9 @@ protected: QVector &labelPositions() const; QStringList &labelStrings() const; + void setLocale(const QLocale &locale); + QLocale locale() const; + QScopedPointer d_ptr; private: diff --git a/src/datavisualization/axis/qvalue3daxisformatter_p.h b/src/datavisualization/axis/qvalue3daxisformatter_p.h index 2d1dc920..9571d001 100644 --- a/src/datavisualization/axis/qvalue3daxisformatter_p.h +++ b/src/datavisualization/axis/qvalue3daxisformatter_p.h @@ -32,6 +32,7 @@ #include "datavisualizationglobal_p.h" #include "qvalue3daxisformatter.h" #include "utils_p.h" +#include QT_BEGIN_NAMESPACE_DATAVISUALIZATION @@ -83,6 +84,13 @@ protected: bool m_allowNegatives; bool m_allowZero; + QLocale m_locale; + QString m_formatPreStr; + QString m_formatPostStr; + int m_formatPrecision; + char m_formatSpec; + bool m_cLocaleInUse; + friend class QValue3DAxisFormatter; }; diff --git a/src/datavisualization/data/qbar3dseries.cpp b/src/datavisualization/data/qbar3dseries.cpp index 19c3bf79..0aa60b66 100644 --- a/src/datavisualization/data/qbar3dseries.cpp +++ b/src/datavisualization/data/qbar3dseries.cpp @@ -43,26 +43,27 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION * \row * \li @valueTitle \li Title from value axis * \row - * \li @rowIdx \li Visible row index + * \li @rowIdx \li Visible row index. Localized using graph locale. * \row - * \li @colIdx \li Visible Column index + * \li @colIdx \li Visible Column index. Localized using graph locale. * \row * \li @rowLabel \li Label from row axis * \row * \li @colLabel \li Label from column axis * \row - * \li @valueLabel \li Item value formatted using the same format the value axis attached to the graph uses, - * see \l{QValue3DAxis::setLabelFormat()} for more information. + * \li @valueLabel \li Item value formatted using the same format the value axis attached to + * the graph uses. See \l{QValue3DAxis::labelFormat} for more information. * \row * \li @seriesName \li Name of the series * \row - * \li % \li Item value in specified format. + * \li % \li Item value in specified format. Formatted using the same rules as + * \l{QValue3DAxis::labelFormat}. * \endtable * * For example: * \snippet doc_src_qtdatavisualization.cpp 1 * - * \sa {Qt Data Visualization Data Handling} + * \sa {Qt Data Visualization Data Handling}, QAbstract3DGraph::locale */ /*! @@ -334,6 +335,10 @@ void QBar3DSeriesPrivate::createItemLabel() return; } + QLocale locale(QLocale::c()); + if (m_controller) + locale = m_controller->locale(); + QCategory3DAxis *categoryAxisZ = static_cast(m_controller->axisZ()); QCategory3DAxis *categoryAxisX = static_cast(m_controller->axisX()); QValue3DAxis *valueAxis = static_cast(m_controller->axisY()); @@ -344,13 +349,13 @@ void QBar3DSeriesPrivate::createItemLabel() int selBarPosRow = m_selectedBar.x(); int selBarPosCol = m_selectedBar.y(); - m_itemLabel.replace(rowIndexTag, QString::number(selBarPosRow)); + m_itemLabel.replace(rowIndexTag, locale.toString(selBarPosRow)); if (categoryAxisZ->labels().size() > selBarPosRow) m_itemLabel.replace(rowLabelTag, categoryAxisZ->labels().at(selBarPosRow)); else m_itemLabel.replace(rowLabelTag, QString()); m_itemLabel.replace(rowTitleTag, categoryAxisZ->title()); - m_itemLabel.replace(colIndexTag, QString::number(selBarPosCol)); + m_itemLabel.replace(colIndexTag, locale.toString(selBarPosCol)); if (categoryAxisX->labels().size() > selBarPosCol) m_itemLabel.replace(colLabelTag, categoryAxisX->labels().at(selBarPosCol)); else diff --git a/src/datavisualization/doc/src/qtdatavisualization-qml-abstractdeclarative.qdoc b/src/datavisualization/doc/src/qtdatavisualization-qml-abstractdeclarative.qdoc index b1d74dd6..5f06d404 100644 --- a/src/datavisualization/doc/src/qtdatavisualization-qml-abstractdeclarative.qdoc +++ b/src/datavisualization/doc/src/qtdatavisualization-qml-abstractdeclarative.qdoc @@ -375,3 +375,13 @@ * * \sa reflection */ + +/*! + * \qmlproperty locale AbstractGraph3D::locale + * \since QtDataVisualization 1.2 + * + * Sets the locale used for formatting various numeric labels. + * Defaults to \c{"C"} locale. + * + * \sa ValueAxis3D::labelFormat + */ diff --git a/src/datavisualization/engine/abstract3dcontroller.cpp b/src/datavisualization/engine/abstract3dcontroller.cpp index feb028cc..e2f8c1ad 100644 --- a/src/datavisualization/engine/abstract3dcontroller.cpp +++ b/src/datavisualization/engine/abstract3dcontroller.cpp @@ -42,6 +42,7 @@ Abstract3DController::Abstract3DController(QRect initialViewport, Q3DScene *scen m_optimizationHints(QAbstract3DGraph::OptimizationDefault), m_reflectionEnabled(false), m_reflectivity(0.5), + m_locale(QLocale::c()), m_scene(scene), m_activeInputHandler(0), m_axisX(0), @@ -1415,6 +1416,8 @@ void Abstract3DController::setAxisHelper(QAbstract3DAxis::AxisOrientation orient handleAxisLabelFormatChangedBySender(valueAxis); handleAxisReversedChangedBySender(valueAxis); handleAxisFormatterDirtyBySender(valueAxis->dptr()); + + valueAxis->formatter()->setLocale(m_locale); } } @@ -1632,4 +1635,28 @@ float Abstract3DController::radialLabelOffset() const return m_radialLabelOffset; } +void Abstract3DController::setLocale(const QLocale &locale) +{ + if (m_locale != locale) { + m_locale = locale; + + // Value axis formatters need to be updated + QValue3DAxis *axis = qobject_cast(m_axisX); + if (axis) + axis->formatter()->setLocale(m_locale); + axis = qobject_cast(m_axisY); + if (axis) + axis->formatter()->setLocale(m_locale); + axis = qobject_cast(m_axisZ); + if (axis) + axis->formatter()->setLocale(m_locale); + emit localeChanged(m_locale); + } +} + +QLocale Abstract3DController::locale() const +{ + return m_locale; +} + QT_END_NAMESPACE_DATAVISUALIZATION diff --git a/src/datavisualization/engine/abstract3dcontroller_p.h b/src/datavisualization/engine/abstract3dcontroller_p.h index 3397fc62..03061a13 100644 --- a/src/datavisualization/engine/abstract3dcontroller_p.h +++ b/src/datavisualization/engine/abstract3dcontroller_p.h @@ -38,6 +38,7 @@ #include "qcustom3ditem.h" #include #include +#include class QOpenGLFramebufferObject; @@ -171,6 +172,7 @@ private: QAbstract3DGraph::OptimizationHints m_optimizationHints; bool m_reflectionEnabled; qreal m_reflectivity; + QLocale m_locale; protected: Q3DScene *m_scene; @@ -297,6 +299,9 @@ public: void setRadialLabelOffset(float offset); float radialLabelOffset() const; + void setLocale(const QLocale &locale); + QLocale locale() const; + void emitNeedRender(); virtual void clearSelection() = 0; @@ -378,6 +383,7 @@ signals: void radialLabelOffsetChanged(float offset); void reflectionChanged(bool enabled); void reflectivityChanged(qreal reflectivity); + void localeChanged(const QLocale &locale); protected: virtual QAbstract3DAxis *createDefaultAxis(QAbstract3DAxis::AxisOrientation orientation); diff --git a/src/datavisualization/engine/abstract3drenderer_p.h b/src/datavisualization/engine/abstract3drenderer_p.h index b31cfbd3..2072314b 100644 --- a/src/datavisualization/engine/abstract3drenderer_p.h +++ b/src/datavisualization/engine/abstract3drenderer_p.h @@ -287,6 +287,8 @@ protected: bool m_reflectionEnabled; qreal m_reflectivity; + QLocale m_locale; + private: friend class Abstract3DController; }; diff --git a/src/datavisualization/engine/qabstract3dgraph.cpp b/src/datavisualization/engine/qabstract3dgraph.cpp index d906666c..8ced09fe 100644 --- a/src/datavisualization/engine/qabstract3dgraph.cpp +++ b/src/datavisualization/engine/qabstract3dgraph.cpp @@ -766,6 +766,25 @@ qreal QAbstract3DGraph::reflectivity() const return d_ptr->m_visualController->reflectivity(); } +/*! + * \property QAbstract3DGraph::locale + * \since QtDataVisualization 1.2 + * + * Sets the locale used for formatting various numeric labels. + * Defaults to \c{"C"} locale. + * + * \sa QValue3DAxis::labelFormat + */ +void QAbstract3DGraph::setLocale(const QLocale &locale) +{ + d_ptr->m_visualController->setLocale(locale); +} + +QLocale QAbstract3DGraph::locale() const +{ + return d_ptr->m_visualController->locale(); +} + /*! * \internal */ @@ -927,6 +946,8 @@ void QAbstract3DGraphPrivate::setVisualController(Abstract3DController *controll &QAbstract3DGraph::reflectionChanged); QObject::connect(m_visualController, &Abstract3DController::reflectivityChanged, q_ptr, &QAbstract3DGraph::reflectivityChanged); + QObject::connect(m_visualController, &Abstract3DController::localeChanged, q_ptr, + &QAbstract3DGraph::localeChanged); } void QAbstract3DGraphPrivate::handleDevicePixelRatioChange() diff --git a/src/datavisualization/engine/qabstract3dgraph.h b/src/datavisualization/engine/qabstract3dgraph.h index e19a3e8e..2d76f780 100644 --- a/src/datavisualization/engine/qabstract3dgraph.h +++ b/src/datavisualization/engine/qabstract3dgraph.h @@ -25,6 +25,7 @@ #include #include #include +#include QT_BEGIN_NAMESPACE_DATAVISUALIZATION @@ -55,6 +56,7 @@ class QT_DATAVISUALIZATION_EXPORT QAbstract3DGraph : public QWindow, protected Q Q_PROPERTY(qreal horizontalAspectRatio READ horizontalAspectRatio WRITE setHorizontalAspectRatio NOTIFY horizontalAspectRatioChanged) Q_PROPERTY(bool reflection READ isReflection WRITE setReflection NOTIFY reflectionChanged) Q_PROPERTY(qreal reflectivity READ reflectivity WRITE setReflectivity NOTIFY reflectivityChanged) + Q_PROPERTY(QLocale locale READ locale WRITE setLocale NOTIFY localeChanged) protected: explicit QAbstract3DGraph(QAbstract3DGraphPrivate *d, const QSurfaceFormat *format, @@ -170,6 +172,9 @@ public: void setReflectivity(qreal reflectivity); qreal reflectivity() const; + void setLocale(const QLocale &locale); + QLocale locale() const; + protected: bool event(QEvent *event); void resizeEvent(QResizeEvent *event); @@ -198,6 +203,7 @@ signals: void horizontalAspectRatioChanged(qreal ratio); void reflectionChanged(bool enabled); void reflectivityChanged(qreal reflectivity); + void localeChanged(const QLocale &locale); private: Q_DISABLE_COPY(QAbstract3DGraph) diff --git a/src/datavisualization/utils/utils.cpp b/src/datavisualization/utils/utils.cpp index d7b8dae8..b2bae478 100644 --- a/src/datavisualization/utils/utils.cpp +++ b/src/datavisualization/utils/utils.cpp @@ -152,46 +152,54 @@ QImage Utils::getGradientImage(QLinearGradient &gradient) return image; } -Utils::ParamType Utils::mapFormatCharToParamType(const QChar &formatChar) +Utils::ParamType Utils::preParseFormat(const QString &format, QString &preStr, QString &postStr, + int &precision, char &formatSpec) { - ParamType retVal = ParamTypeUnknown; - if (formatChar == QLatin1Char('d') - || formatChar == QLatin1Char('i') - || formatChar == QLatin1Char('c')) { - retVal = ParamTypeInt; - } else if (formatChar == QLatin1Char('u') - || formatChar == QLatin1Char('o') - || formatChar == QLatin1Char('x') - || formatChar == QLatin1Char('X')) { - retVal = ParamTypeUInt; - } else if (formatChar == QLatin1Char('f') - || formatChar == QLatin1Char('F') - || formatChar == QLatin1Char('e') - || formatChar == QLatin1Char('E') - || formatChar == QLatin1Char('g') - || formatChar == QLatin1Char('G')) { - retVal = ParamTypeReal; + static QRegExp formatMatcher(QStringLiteral("^([^%]*)%([\\-\\+#\\s\\d\\.lhjztL]*)([dicuoxfegXFEG])(.*)$")); + static QRegExp precisionMatcher(QStringLiteral("\\.(\\d+)")); + + Utils::ParamType retVal; + + if (formatMatcher.indexIn(format, 0) != -1) { + preStr = formatMatcher.cap(1); + // Six and 'g' are defaults in Qt API + precision = 6; + if (!formatMatcher.cap(2).isEmpty()) { + if (precisionMatcher.indexIn(formatMatcher.cap(2), 0) != -1) + precision = precisionMatcher.cap(1).toInt(); + } + if (formatMatcher.cap(3).isEmpty()) + formatSpec = 'g'; + else + formatSpec = formatMatcher.cap(3).at(0).toLatin1(); + postStr = formatMatcher.cap(4); + retVal = mapFormatCharToParamType(formatSpec); + } else { + retVal = ParamTypeUnknown; + // The out parameters are irrelevant in unknown case } return retVal; } -Utils::ParamType Utils::findFormatParamType(const QString &format) +Utils::ParamType Utils::mapFormatCharToParamType(char formatSpec) { - static QRegExp formatMatcher(QStringLiteral("%[\\-\\+#\\s\\d\\.lhjztL]*([dicuoxfegXFEG])")); - - if (formatMatcher.indexIn(format, 0) != -1) { - QString capStr = formatMatcher.cap(1); - if (capStr.isEmpty()) - return ParamTypeUnknown; - else - return mapFormatCharToParamType(capStr.at(0)); + ParamType retVal = ParamTypeUnknown; + if (formatSpec == 'd' || formatSpec == 'i' || formatSpec == 'c') { + retVal = ParamTypeInt; + } else if (formatSpec == 'u' || formatSpec == 'o' + || formatSpec == 'x'|| formatSpec == 'X') { + retVal = ParamTypeUInt; + } else if (formatSpec == 'f' || formatSpec == 'F' + || formatSpec == 'e' || formatSpec == 'E' + || formatSpec == 'g' || formatSpec == 'G') { + retVal = ParamTypeReal; } - return ParamTypeUnknown; + return retVal; } -QString Utils::formatLabel(const QByteArray &format, ParamType paramType, qreal value) +QString Utils::formatLabelSprintf(const QByteArray &format, Utils::ParamType paramType, qreal value) { switch (paramType) { case ParamTypeInt: @@ -201,7 +209,24 @@ QString Utils::formatLabel(const QByteArray &format, ParamType paramType, qreal case ParamTypeReal: return QString().sprintf(format, value); default: - return QString::fromUtf8(format); // To detect errors + // Return format string to detect errors. Bars selection label logic also depends on this. + return QString::fromUtf8(format); + } +} + +QString Utils::formatLabelLocalized(Utils::ParamType paramType, qreal value, + const QLocale &locale, const QString &preStr, const QString &postStr, + int precision, char formatSpec, const QByteArray &format) +{ + switch (paramType) { + case ParamTypeInt: + case ParamTypeUInt: + return preStr + locale.toString(qint64(value)) + postStr; + case ParamTypeReal: + return preStr + locale.toString(value, formatSpec, precision) + postStr; + default: + // Return format string to detect errors. Bars selection label logic also depends on this. + return QString::fromUtf8(format); } } diff --git a/src/datavisualization/utils/utils_p.h b/src/datavisualization/utils/utils_p.h index 55707fdb..805c41a7 100644 --- a/src/datavisualization/utils/utils_p.h +++ b/src/datavisualization/utils/utils_p.h @@ -59,15 +59,19 @@ public: static QVector4D getSelection(QPoint mousepos, int height); static QImage getGradientImage(QLinearGradient &gradient); - static ParamType findFormatParamType(const QString &format); - static QString formatLabel(const QByteArray &format, ParamType paramType, qreal value); + static ParamType preParseFormat(const QString &format, QString &preStr, QString &postStr, + int &precision, char &formatSpec); + static QString formatLabelSprintf(const QByteArray &format, ParamType paramType, qreal value); + static QString formatLabelLocalized(ParamType paramType, qreal value, + const QLocale &locale, const QString &preStr, const QString &postStr, + int precision, char formatSpec, const QByteArray &format); static QString defaultLabelFormat(); static float wrapValue(float value, float min, float max); static QQuaternion calculateRotation(const QVector3D &xyzRotations); private: - static ParamType mapFormatCharToParamType(const QChar &formatChar); + static ParamType mapFormatCharToParamType(char formatSpec); }; QT_END_NAMESPACE_DATAVISUALIZATION diff --git a/src/datavisualizationqml2/abstractdeclarative.cpp b/src/datavisualizationqml2/abstractdeclarative.cpp index b062f78c..fdfa55d8 100644 --- a/src/datavisualizationqml2/abstractdeclarative.cpp +++ b/src/datavisualizationqml2/abstractdeclarative.cpp @@ -335,6 +335,12 @@ void AbstractDeclarative::setSharedController(Abstract3DController *controller) &AbstractDeclarative::radialLabelOffsetChanged); QObject::connect(m_controller.data(), &Abstract3DController::horizontalAspectRatioChanged, this, &AbstractDeclarative::horizontalAspectRatioChanged); + QObject::connect(m_controller.data(), &Abstract3DController::reflectionChanged, this, + &AbstractDeclarative::reflectionChanged); + QObject::connect(m_controller.data(), &Abstract3DController::reflectivityChanged, this, + &AbstractDeclarative::reflectivityChanged); + QObject::connect(m_controller.data(), &Abstract3DController::localeChanged, this, + &AbstractDeclarative::localeChanged); } void AbstractDeclarative::activateOpenGLContext(QQuickWindow *window) @@ -780,6 +786,16 @@ qreal AbstractDeclarative::reflectivity() const return m_controller->reflectivity(); } +void AbstractDeclarative::setLocale(const QLocale &locale) +{ + m_controller->setLocale(locale); +} + +QLocale AbstractDeclarative::locale() const +{ + return m_controller->locale(); +} + void AbstractDeclarative::windowDestroyed(QObject *obj) { // Remove destroyed window from window lists diff --git a/src/datavisualizationqml2/abstractdeclarative_p.h b/src/datavisualizationqml2/abstractdeclarative_p.h index f4bc41b7..52a5956b 100644 --- a/src/datavisualizationqml2/abstractdeclarative_p.h +++ b/src/datavisualizationqml2/abstractdeclarative_p.h @@ -77,6 +77,7 @@ class AbstractDeclarative : public QQuickItem Q_PROPERTY(qreal horizontalAspectRatio READ horizontalAspectRatio WRITE setHorizontalAspectRatio NOTIFY horizontalAspectRatioChanged REVISION 2) Q_PROPERTY(bool reflection READ isReflection WRITE setReflection NOTIFY reflectionChanged REVISION 2) Q_PROPERTY(qreal reflectivity READ reflectivity WRITE setReflectivity NOTIFY reflectivityChanged REVISION 2) + Q_PROPERTY(QLocale locale READ locale WRITE setLocale NOTIFY localeChanged REVISION 2) public: enum SelectionFlag { @@ -213,6 +214,9 @@ public: void setReflectivity(qreal reflectivity); qreal reflectivity() const; + void setLocale(const QLocale &locale); + QLocale locale() const; + public slots: virtual void handleAxisXChanged(QAbstract3DAxis *axis) = 0; virtual void handleAxisYChanged(QAbstract3DAxis *axis) = 0; @@ -255,6 +259,7 @@ signals: Q_REVISION(2) void horizontalAspectRatioChanged(qreal ratio); Q_REVISION(2) void reflectionChanged(bool enabled); Q_REVISION(2) void reflectivityChanged(qreal reflectivity); + Q_REVISION(2) void localeChanged(const QLocale &locale); private: QPointer m_controller; -- cgit v1.2.3 From 105e85170d35f7086dc96d1853298fcf8d680e06 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Fri, 19 Sep 2014 11:05:27 +0300 Subject: Change custom labels to use label shader MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This fixes the problem with specular highlight making camera-facing labels unreadable. Task-number: QTRD-3276 Change-Id: If16424b07a1f3362428980353c5fb5213c33d06b Reviewed-by: Tomi Korpipää --- .../datavisualization/volumetric/volumetric.cpp | 138 +++++++++++---------- .../engine/abstract3drenderer.cpp | 22 +++- .../engine/abstract3drenderer_p.h | 3 + src/datavisualization/engine/bars3drenderer.cpp | 12 -- src/datavisualization/engine/bars3drenderer_p.h | 2 - src/datavisualization/engine/scatter3drenderer.cpp | 13 -- src/datavisualization/engine/scatter3drenderer_p.h | 2 - src/datavisualization/engine/surface3drenderer.cpp | 12 -- src/datavisualization/engine/surface3drenderer_p.h | 2 - 9 files changed, 95 insertions(+), 111 deletions(-) diff --git a/examples/datavisualization/volumetric/volumetric.cpp b/examples/datavisualization/volumetric/volumetric.cpp index 6672d964..cbbaf2a1 100644 --- a/examples/datavisualization/volumetric/volumetric.cpp +++ b/examples/datavisualization/volumetric/volumetric.cpp @@ -198,7 +198,6 @@ VolumetricModifier::VolumetricModifier(Q3DScatter *scatter) warningLabel->setPositionAbsolute(true); warningLabel->setFacingCamera(true); m_graph->addCustomItem(warningLabel); - m_graph->activeTheme()->setLightStrength(1.0f); #endif QObject::connect(m_graph->scene()->activeCamera(), &Q3DCamera::zoomLevelChanged, this, @@ -266,10 +265,10 @@ void VolumetricModifier::sliceZ(int enabled) void VolumetricModifier::adjustSliceX(int value) { - m_sliceIndexX = value / (1024 / m_volumeItem->textureWidth()); - if (m_sliceIndexX == m_volumeItem->textureWidth()) - m_sliceIndexX--; if (m_volumeItem) { + m_sliceIndexX = value / (1024 / m_volumeItem->textureWidth()); + if (m_sliceIndexX == m_volumeItem->textureWidth()) + m_sliceIndexX--; if (m_volumeItem->sliceIndexX() != -1) //! [7] m_volumeItem->setSliceIndexX(m_sliceIndexX); @@ -283,10 +282,10 @@ void VolumetricModifier::adjustSliceX(int value) void VolumetricModifier::adjustSliceY(int value) { - m_sliceIndexY = value / (1024 / m_volumeItem->textureHeight()); - if (m_sliceIndexY == m_volumeItem->textureHeight()) - m_sliceIndexY--; if (m_volumeItem) { + m_sliceIndexY = value / (1024 / m_volumeItem->textureHeight()); + if (m_sliceIndexY == m_volumeItem->textureHeight()) + m_sliceIndexY--; if (m_volumeItem->sliceIndexY() != -1) m_volumeItem->setSliceIndexY(m_sliceIndexY); m_sliceLabelY->setPixmap( @@ -296,10 +295,10 @@ void VolumetricModifier::adjustSliceY(int value) void VolumetricModifier::adjustSliceZ(int value) { - m_sliceIndexZ = value / (1024 / m_volumeItem->textureDepth()); - if (m_sliceIndexZ == m_volumeItem->textureDepth()) - m_sliceIndexZ--; - if (m_volumeItem) { + if (m_volumeItem) { + m_sliceIndexZ = value / (1024 / m_volumeItem->textureDepth()); + if (m_sliceIndexZ == m_volumeItem->textureDepth()) + m_sliceIndexZ--; if (m_volumeItem->sliceIndexZ() != -1) m_volumeItem->setSliceIndexZ(m_sliceIndexZ); m_sliceLabelZ->setPixmap( @@ -353,7 +352,7 @@ void VolumetricModifier::handleTimeout() void VolumetricModifier::toggleLowDetail(bool enabled) { - if (enabled) { + if (enabled && m_volumeItem) { m_volumeItem->setTextureData(new QVector(*m_lowDetailData)); m_volumeItem->setTextureDimensions(lowDetailSize, lowDetailSize / 2, lowDetailSize); adjustSliceX(m_sliceSliderX->value()); @@ -364,7 +363,7 @@ void VolumetricModifier::toggleLowDetail(bool enabled) void VolumetricModifier::toggleMediumDetail(bool enabled) { - if (enabled) { + if (enabled && m_volumeItem) { m_volumeItem->setTextureData(new QVector(*m_mediumDetailData)); m_volumeItem->setTextureDimensions(mediumDetailSize, mediumDetailSize / 2, mediumDetailSize); adjustSliceX(m_sliceSliderX->value()); @@ -375,7 +374,7 @@ void VolumetricModifier::toggleMediumDetail(bool enabled) void VolumetricModifier::toggleHighDetail(bool enabled) { - if (enabled) { + if (enabled && m_volumeItem) { m_volumeItem->setTextureData(new QVector(*m_highDetailData)); m_volumeItem->setTextureDimensions(highDetailSize, highDetailSize / 2, highDetailSize); adjustSliceX(m_sliceSliderX->value()); @@ -407,76 +406,87 @@ void VolumetricModifier::setSliceSliders(QSlider *sliderX, QSlider *sliderY, QSl void VolumetricModifier::changeColorTable(int enabled) { - if (enabled) - m_volumeItem->setColorTable(m_colorTable2); - else - m_volumeItem->setColorTable(m_colorTable1); + if (m_volumeItem) { + if (enabled) + m_volumeItem->setColorTable(m_colorTable2); + else + m_volumeItem->setColorTable(m_colorTable1); - m_usingPrimaryTable = !enabled; + m_usingPrimaryTable = !enabled; - // Rerender image labels - adjustSliceX(m_sliceSliderX->value()); - adjustSliceY(m_sliceSliderY->value()); - adjustSliceZ(m_sliceSliderZ->value()); + // Rerender image labels + adjustSliceX(m_sliceSliderX->value()); + adjustSliceY(m_sliceSliderY->value()); + adjustSliceZ(m_sliceSliderZ->value()); + } } void VolumetricModifier::setPreserveOpacity(bool enabled) { - //! [10] - m_volumeItem->setPreserveOpacity(enabled); - //! [10] - // Rerender image labels - adjustSliceX(m_sliceSliderX->value()); - adjustSliceY(m_sliceSliderY->value()); - adjustSliceZ(m_sliceSliderZ->value()); + if (m_volumeItem) { + //! [10] + m_volumeItem->setPreserveOpacity(enabled); + //! [10] + + // Rerender image labels + adjustSliceX(m_sliceSliderX->value()); + adjustSliceY(m_sliceSliderY->value()); + adjustSliceZ(m_sliceSliderZ->value()); + } } void VolumetricModifier::setTransparentGround(bool enabled) { - //! [12] - int newAlpha = enabled ? terrainTransparency : 255; - for (int i = aboveWaterGroundColorsMin; i < underWaterGroundColorsMax; i++) { - QRgb oldColor1 = m_colorTable1.at(i); - QRgb oldColor2 = m_colorTable2.at(i); - m_colorTable1[i] = qRgba(qRed(oldColor1), qGreen(oldColor1), qBlue(oldColor1), newAlpha); - m_colorTable2[i] = qRgba(qRed(oldColor2), qGreen(oldColor2), qBlue(oldColor2), newAlpha); + if (m_volumeItem) { + //! [12] + int newAlpha = enabled ? terrainTransparency : 255; + for (int i = aboveWaterGroundColorsMin; i < underWaterGroundColorsMax; i++) { + QRgb oldColor1 = m_colorTable1.at(i); + QRgb oldColor2 = m_colorTable2.at(i); + m_colorTable1[i] = qRgba(qRed(oldColor1), qGreen(oldColor1), qBlue(oldColor1), newAlpha); + m_colorTable2[i] = qRgba(qRed(oldColor2), qGreen(oldColor2), qBlue(oldColor2), newAlpha); + } + if (m_usingPrimaryTable) + m_volumeItem->setColorTable(m_colorTable1); + else + m_volumeItem->setColorTable(m_colorTable2); + //! [12] + adjustSliceX(m_sliceSliderX->value()); + adjustSliceY(m_sliceSliderY->value()); + adjustSliceZ(m_sliceSliderZ->value()); } - if (m_usingPrimaryTable) - m_volumeItem->setColorTable(m_colorTable1); - else - m_volumeItem->setColorTable(m_colorTable2); - //! [12] - adjustSliceX(m_sliceSliderX->value()); - adjustSliceY(m_sliceSliderY->value()); - adjustSliceZ(m_sliceSliderZ->value()); } void VolumetricModifier::setUseHighDefShader(bool enabled) { - //! [13] - m_volumeItem->setUseHighDefShader(enabled); - //! [13] + if (m_volumeItem) { + //! [13] + m_volumeItem->setUseHighDefShader(enabled); + //! [13] + } } void VolumetricModifier::adjustAlphaMultiplier(int value) { - float mult; - if (value > 100) - mult = float(value - 99) / 2.0f; - else - mult = float(value) / float(500 - value * 4); - //! [11] - m_volumeItem->setAlphaMultiplier(mult); - //! [11] - QString labelFormat = QStringLiteral("Alpha multiplier: %1"); - m_alphaMultiplierLabel->setText(labelFormat.arg( - QString::number(m_volumeItem->alphaMultiplier(), 'f', 3))); - - // Rerender image labels - adjustSliceX(m_sliceSliderX->value()); - adjustSliceY(m_sliceSliderY->value()); - adjustSliceZ(m_sliceSliderZ->value()); + if (m_volumeItem) { + float mult; + if (value > 100) + mult = float(value - 99) / 2.0f; + else + mult = float(value) / float(500 - value * 4); + //! [11] + m_volumeItem->setAlphaMultiplier(mult); + //! [11] + QString labelFormat = QStringLiteral("Alpha multiplier: %1"); + m_alphaMultiplierLabel->setText(labelFormat.arg( + QString::number(m_volumeItem->alphaMultiplier(), 'f', 3))); + + // Rerender image labels + adjustSliceX(m_sliceSliderX->value()); + adjustSliceY(m_sliceSliderY->value()); + adjustSliceZ(m_sliceSliderZ->value()); + } } void VolumetricModifier::toggleAreaAll(bool enabled) diff --git a/src/datavisualization/engine/abstract3drenderer.cpp b/src/datavisualization/engine/abstract3drenderer.cpp index a0e0bc45..d0f3c04b 100644 --- a/src/datavisualization/engine/abstract3drenderer.cpp +++ b/src/datavisualization/engine/abstract3drenderer.cpp @@ -63,6 +63,7 @@ Abstract3DRenderer::Abstract3DRenderer(Abstract3DController *controller) m_volumeTextureLowDefShader(0), m_volumeTextureSliceShader(0), m_volumeSliceFrameShader(0), + m_labelShader(0), m_useOrthoProjection(false), m_xFlipped(false), m_yFlipped(false), @@ -108,6 +109,7 @@ Abstract3DRenderer::~Abstract3DRenderer() delete m_volumeTextureLowDefShader; delete m_volumeSliceFrameShader; delete m_volumeTextureSliceShader; + delete m_labelShader; foreach (SeriesRenderCache *cache, m_renderCacheList) { cache->cleanup(m_textureHelper); @@ -152,6 +154,9 @@ void Abstract3DRenderer::initializeOpenGL() axisCacheForOrientation(QAbstract3DAxis::AxisOrientationX).setDrawer(m_drawer); axisCacheForOrientation(QAbstract3DAxis::AxisOrientationY).setDrawer(m_drawer); axisCacheForOrientation(QAbstract3DAxis::AxisOrientationZ).setDrawer(m_drawer); + + initLabelShaders(QStringLiteral(":/shaders/vertexLabel"), + QStringLiteral(":/shaders/fragmentLabel")); } void Abstract3DRenderer::render(const GLuint defaultFboHandle) @@ -232,6 +237,13 @@ void Abstract3DRenderer::initVolumeTextureShaders(const QString &vertexShader, m_volumeSliceFrameShader->initialize(); } +void Abstract3DRenderer::initLabelShaders(const QString &vertexShader, const QString &fragmentShader) +{ + delete m_labelShader; + m_labelShader = new ShaderHelper(this, vertexShader, fragmentShader); + m_labelShader->initialize(); +} + void Abstract3DRenderer::updateTheme(Q3DTheme *theme) { // Synchronize the controller theme with renderer @@ -1366,8 +1378,8 @@ void Abstract3DRenderer::drawCustomItems(RenderingState state, if (RenderingNormal == state) { // Normal render -#if !defined(QT_OPENGL_ES_2) ShaderHelper *prevShader = shader; +#if !defined(QT_OPENGL_ES_2) if (item->isVolume()) { if (item->drawSlices() && (item->sliceIndexX() >= 0 @@ -1379,12 +1391,14 @@ void Abstract3DRenderer::drawCustomItems(RenderingState state, } else { shader = m_volumeTextureLowDefShader; } - } else { + } else +#endif + if (item->isLabel()) + shader = m_labelShader; + else shader = regularShader; - } if (shader != prevShader) shader->bind(); -#endif shader->setUniformValue(shader->model(), modelMatrix); shader->setUniformValue(shader->MVP(), MVPMatrix); shader->setUniformValue(shader->nModel(), itModelMatrix.inverted().transposed()); diff --git a/src/datavisualization/engine/abstract3drenderer_p.h b/src/datavisualization/engine/abstract3drenderer_p.h index 2072314b..a46e6176 100644 --- a/src/datavisualization/engine/abstract3drenderer_p.h +++ b/src/datavisualization/engine/abstract3drenderer_p.h @@ -98,6 +98,8 @@ public: const QString &sliceShader, const QString &sliceFrameVertexShader, const QString &sliceFrameShader); + virtual void initLabelShaders(const QString &vertexShader, const QString &fragmentShader); + virtual void updateAxisType(QAbstract3DAxis::AxisOrientation orientation, QAbstract3DAxis::AxisType type); virtual void updateAxisTitle(QAbstract3DAxis::AxisOrientation orientation, @@ -253,6 +255,7 @@ protected: ShaderHelper *m_volumeTextureLowDefShader; ShaderHelper *m_volumeTextureSliceShader; ShaderHelper *m_volumeSliceFrameShader; + ShaderHelper *m_labelShader; bool m_useOrthoProjection; bool m_xFlipped; diff --git a/src/datavisualization/engine/bars3drenderer.cpp b/src/datavisualization/engine/bars3drenderer.cpp index 1f395422..e9cd9d43 100644 --- a/src/datavisualization/engine/bars3drenderer.cpp +++ b/src/datavisualization/engine/bars3drenderer.cpp @@ -47,7 +47,6 @@ Bars3DRenderer::Bars3DRenderer(Bars3DController *controller) m_depthShader(0), m_selectionShader(0), m_backgroundShader(0), - m_labelShader(0), m_bgrTexture(0), m_selectionTexture(0), m_depthFrameBuffer(0), @@ -102,7 +101,6 @@ Bars3DRenderer::~Bars3DRenderer() delete m_depthShader; delete m_selectionShader; delete m_backgroundShader; - delete m_labelShader; } void Bars3DRenderer::initializeOpenGL() @@ -110,8 +108,6 @@ void Bars3DRenderer::initializeOpenGL() Abstract3DRenderer::initializeOpenGL(); // Initialize shaders - initLabelShaders(QStringLiteral(":/shaders/vertexLabel"), - QStringLiteral(":/shaders/fragmentLabel")); #if !defined(QT_OPENGL_ES_2) // Init depth shader (for shadows). Init in any case, easier to handle shadow activation if done via api. @@ -2814,14 +2810,6 @@ void Bars3DRenderer::initBackgroundShaders(const QString &vertexShader, m_backgroundShader->initialize(); } -void Bars3DRenderer::initLabelShaders(const QString &vertexShader, const QString &fragmentShader) -{ - if (m_labelShader) - delete m_labelShader; - m_labelShader = new ShaderHelper(this, vertexShader, fragmentShader); - m_labelShader->initialize(); -} - QVector3D Bars3DRenderer::convertPositionToTranslation(const QVector3D &position, bool isAbsolute) { float xTrans = 0.0f; diff --git a/src/datavisualization/engine/bars3drenderer_p.h b/src/datavisualization/engine/bars3drenderer_p.h index 7f1e83bb..47c9ab1d 100644 --- a/src/datavisualization/engine/bars3drenderer_p.h +++ b/src/datavisualization/engine/bars3drenderer_p.h @@ -66,7 +66,6 @@ private: ShaderHelper *m_depthShader; ShaderHelper *m_selectionShader; ShaderHelper *m_backgroundShader; - ShaderHelper *m_labelShader; GLuint m_bgrTexture; GLuint m_selectionTexture; GLuint m_depthFrameBuffer; @@ -164,7 +163,6 @@ private: void loadBackgroundMesh(); void initSelectionShader(); void initBackgroundShaders(const QString &vertexShader, const QString &fragmentShader); - void initLabelShaders(const QString &vertexShader, const QString &fragmentShader); void initSelectionBuffer(); #if !defined(QT_OPENGL_ES_2) void initDepthShader(); diff --git a/src/datavisualization/engine/scatter3drenderer.cpp b/src/datavisualization/engine/scatter3drenderer.cpp index 22d40a74..e9e395be 100644 --- a/src/datavisualization/engine/scatter3drenderer.cpp +++ b/src/datavisualization/engine/scatter3drenderer.cpp @@ -49,7 +49,6 @@ Scatter3DRenderer::Scatter3DRenderer(Scatter3DController *controller) m_depthShader(0), m_selectionShader(0), m_backgroundShader(0), - m_labelShader(0), m_staticGradientPointShader(0), m_bgrTexture(0), m_selectionTexture(0), @@ -93,7 +92,6 @@ Scatter3DRenderer::~Scatter3DRenderer() delete m_depthShader; delete m_selectionShader; delete m_backgroundShader; - delete m_labelShader; delete m_staticGradientPointShader; } @@ -102,8 +100,6 @@ void Scatter3DRenderer::initializeOpenGL() Abstract3DRenderer::initializeOpenGL(); // Initialize shaders - initLabelShaders(QStringLiteral(":/shaders/vertexLabel"), - QStringLiteral(":/shaders/fragmentLabel")); #if !defined(QT_OPENGL_ES_2) // Init depth shader (for shadows). Init in any case, easier to handle shadow activation if done via api. @@ -2260,14 +2256,6 @@ void Scatter3DRenderer::initBackgroundShaders(const QString &vertexShader, m_backgroundShader->initialize(); } -void Scatter3DRenderer::initLabelShaders(const QString &vertexShader, const QString &fragmentShader) -{ - if (m_labelShader) - delete m_labelShader; - m_labelShader = new ShaderHelper(this, vertexShader, fragmentShader); - m_labelShader->initialize(); -} - void Scatter3DRenderer::initStaticPointShaders(const QString &vertexShader, const QString &fragmentShader) { @@ -2277,7 +2265,6 @@ void Scatter3DRenderer::initStaticPointShaders(const QString &vertexShader, m_staticGradientPointShader->initialize(); } - void Scatter3DRenderer::selectionColorToSeriesAndIndex(const QVector4D &color, int &index, QAbstract3DSeries *&series) diff --git a/src/datavisualization/engine/scatter3drenderer_p.h b/src/datavisualization/engine/scatter3drenderer_p.h index 0852f262..d8908e59 100644 --- a/src/datavisualization/engine/scatter3drenderer_p.h +++ b/src/datavisualization/engine/scatter3drenderer_p.h @@ -59,7 +59,6 @@ private: ShaderHelper *m_depthShader; ShaderHelper *m_selectionShader; ShaderHelper *m_backgroundShader; - ShaderHelper *m_labelShader; ShaderHelper *m_staticGradientPointShader; GLuint m_bgrTexture; GLuint m_selectionTexture; @@ -130,7 +129,6 @@ private: void loadBackgroundMesh(); void initSelectionShader(); void initBackgroundShaders(const QString &vertexShader, const QString &fragmentShader); - void initLabelShaders(const QString &vertexShader, const QString &fragmentShader); void initStaticPointShaders(const QString &vertexShader, const QString &fragmentShader); void initSelectionBuffer(); #if !defined(QT_OPENGL_ES_2) diff --git a/src/datavisualization/engine/surface3drenderer.cpp b/src/datavisualization/engine/surface3drenderer.cpp index 9bee6b30..9121e37c 100644 --- a/src/datavisualization/engine/surface3drenderer.cpp +++ b/src/datavisualization/engine/surface3drenderer.cpp @@ -49,7 +49,6 @@ Surface3DRenderer::Surface3DRenderer(Surface3DController *controller) m_surfaceSliceFlatShader(0), m_surfaceSliceSmoothShader(0), m_selectionShader(0), - m_labelShader(0), m_heightNormalizer(0.0f), m_scaleX(0.0f), m_scaleY(0.0f), @@ -110,7 +109,6 @@ Surface3DRenderer::~Surface3DRenderer() delete m_surfaceGridShader; delete m_surfaceSliceFlatShader; delete m_surfaceSliceSmoothShader; - delete m_labelShader; } void Surface3DRenderer::initializeOpenGL() @@ -119,8 +117,6 @@ void Surface3DRenderer::initializeOpenGL() // Initialize shaders initSurfaceShaders(); - initLabelShaders(QStringLiteral(":/shaders/vertexLabel"), - QStringLiteral(":/shaders/fragmentLabel")); #if !defined(QT_OPENGL_ES_2) // Init depth shader (for shadows). Init in any case, easier to handle shadow activation if done via api. @@ -2956,14 +2952,6 @@ void Surface3DRenderer::initSurfaceShaders() handleShadowQualityChange(); } -void Surface3DRenderer::initLabelShaders(const QString &vertexShader, const QString &fragmentShader) -{ - if (m_labelShader) - delete m_labelShader; - m_labelShader = new ShaderHelper(this, vertexShader, fragmentShader); - m_labelShader->initialize(); -} - #if !defined(QT_OPENGL_ES_2) void Surface3DRenderer::initDepthShader() { diff --git a/src/datavisualization/engine/surface3drenderer_p.h b/src/datavisualization/engine/surface3drenderer_p.h index 623951d4..cba1b959 100644 --- a/src/datavisualization/engine/surface3drenderer_p.h +++ b/src/datavisualization/engine/surface3drenderer_p.h @@ -57,7 +57,6 @@ private: ShaderHelper *m_surfaceSliceFlatShader; ShaderHelper *m_surfaceSliceSmoothShader; ShaderHelper *m_selectionShader; - ShaderHelper *m_labelShader; float m_heightNormalizer; float m_scaleX; float m_scaleY; @@ -137,7 +136,6 @@ private: void calculateSceneScalingFactors(); void initBackgroundShaders(const QString &vertexShader, const QString &fragmentShader); - void initLabelShaders(const QString &vertexShader, const QString &fragmentShader); void initSelectionShaders(); void initSurfaceShaders(); void initSelectionBuffer(); -- cgit v1.2.3 From 36bc54f5720bddb9899e64d665117ac1e1b5bc94 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Fri, 19 Sep 2014 13:32:31 +0300 Subject: Allow setting the floor level on bar graphs. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Task-number: QTRD-3313 Change-Id: Ie0f8e36af8698081a6937c360043773da895b6e6 Reviewed-by: Tomi Korpipää --- .../doc/src/qtdatavisualization-qml-bars3d.qdoc | 8 ++++ src/datavisualization/engine/bars3dcontroller.cpp | 20 +++++++++ src/datavisualization/engine/bars3dcontroller_p.h | 7 +++- src/datavisualization/engine/bars3drenderer.cpp | 47 ++++++++++++++-------- src/datavisualization/engine/bars3drenderer_p.h | 3 ++ src/datavisualization/engine/q3dbars.cpp | 20 +++++++++ src/datavisualization/engine/q3dbars.h | 4 ++ .../datavisualizationqml2_plugin.cpp | 1 + src/datavisualizationqml2/declarativebars.cpp | 13 ++++++ src/datavisualizationqml2/declarativebars_p.h | 5 +++ tests/barstest/chart.cpp | 6 +++ tests/barstest/chart.h | 1 + tests/barstest/main.cpp | 11 ++++- tests/qmlcamera/qml/qmlcamera/main.qml | 2 + 14 files changed, 129 insertions(+), 19 deletions(-) diff --git a/src/datavisualization/doc/src/qtdatavisualization-qml-bars3d.qdoc b/src/datavisualization/doc/src/qtdatavisualization-qml-bars3d.qdoc index 6ee51742..0348652a 100644 --- a/src/datavisualization/doc/src/qtdatavisualization-qml-bars3d.qdoc +++ b/src/datavisualization/doc/src/qtdatavisualization-qml-bars3d.qdoc @@ -114,6 +114,14 @@ * Defaults to the first added series or zero if no series are added to the graph. */ +/*! + * \qmlproperty real Bars3D::floorLevel + * + * The desired floor level for the bar graph in Y-axis data coordinates. + * The actual floor level cannot go below Y-axis minimum or above Y-axis maximum. + * Defaults to zero. + */ + /*! * \qmlmethod void Bars3D::addSeries(Bar3DSeries series) * Adds the \a series to the graph. A graph can contain multiple series, but only one set of axes, diff --git a/src/datavisualization/engine/bars3dcontroller.cpp b/src/datavisualization/engine/bars3dcontroller.cpp index bbec6967..2092dd60 100644 --- a/src/datavisualization/engine/bars3dcontroller.cpp +++ b/src/datavisualization/engine/bars3dcontroller.cpp @@ -36,6 +36,7 @@ Bars3DController::Bars3DController(QRect boundRect, Q3DScene *scene) m_isBarSpecRelative(true), m_barThicknessRatio(1.0f), m_barSpacing(QSizeF(1.0, 1.0)), + m_floorLevel(0.0f), m_renderer(0) { // Setting a null axis creates a new default axis according to orientation and graph type. @@ -83,6 +84,12 @@ void Bars3DController::synchDataToRenderer() needSceneUpdate = true; } + // Floor level update requires data update, so do before abstract sync + if (m_changeTracker.floorLevelChanged) { + m_renderer->updateFloorLevel(m_floorLevel); + m_changeTracker.floorLevelChanged = false; + } + Abstract3DController::synchDataToRenderer(); // Notify changes to renderer @@ -485,6 +492,19 @@ bool Bars3DController::isBarSpecRelative() return m_isBarSpecRelative; } +void Bars3DController::setFloorLevel(float level) +{ + m_floorLevel = level; + m_isDataDirty = true; + m_changeTracker.floorLevelChanged = true; + emitNeedRender(); +} + +float Bars3DController::floorLevel() const +{ + return m_floorLevel; +} + void Bars3DController::setSelectionMode(QAbstract3DGraph::SelectionFlags mode) { if (mode.testFlag(QAbstract3DGraph::SelectionSlice) diff --git a/src/datavisualization/engine/bars3dcontroller_p.h b/src/datavisualization/engine/bars3dcontroller_p.h index 4f0e1f20..ac62f37d 100644 --- a/src/datavisualization/engine/bars3dcontroller_p.h +++ b/src/datavisualization/engine/bars3dcontroller_p.h @@ -43,13 +43,15 @@ struct Bars3DChangeBitField { bool selectedBarChanged : 1; bool rowsChanged : 1; bool itemChanged : 1; + bool floorLevelChanged : 1; Bars3DChangeBitField() : multiSeriesScalingChanged(true), barSpecsChanged(true), selectedBarChanged(true), rowsChanged(false), - itemChanged(false) + itemChanged(false), + floorLevelChanged(false) { } }; @@ -84,6 +86,7 @@ private: bool m_isBarSpecRelative; GLfloat m_barThicknessRatio; QSizeF m_barSpacing; + float m_floorLevel; // Rendering Bars3DRenderer *m_renderer; @@ -107,6 +110,8 @@ public: GLfloat barThickness(); QSizeF barSpacing(); bool isBarSpecRelative(); + void setFloorLevel(float level); + float floorLevel() const; inline QBar3DSeries *selectedSeries() const { return m_selectedBarSeries; } diff --git a/src/datavisualization/engine/bars3drenderer.cpp b/src/datavisualization/engine/bars3drenderer.cpp index e9cd9d43..9f3c2e2a 100644 --- a/src/datavisualization/engine/bars3drenderer.cpp +++ b/src/datavisualization/engine/bars3drenderer.cpp @@ -77,7 +77,9 @@ Bars3DRenderer::Bars3DRenderer(Bars3DController *controller) m_haveGradientSeries(false), m_zeroPosition(0.0f), m_xScaleFactor(1.0f), - m_zScaleFactor(1.0f) + m_zScaleFactor(1.0f), + m_floorLevel(0.0f), + m_actualFloorLevel(0.0f) { m_axisCacheY.setScale(2.0f); m_axisCacheY.setTranslate(-1.0f); @@ -209,7 +211,7 @@ void Bars3DRenderer::updateData() calculateSceneScalingFactors(); - m_zeroPosition = m_axisCacheY.formatter()->positionAt(0.0f); + m_zeroPosition = m_axisCacheY.formatter()->positionAt(m_actualFloorLevel); foreach (SeriesRenderCache *baseCache, m_renderCacheList) { BarSeriesRenderCache *cache = static_cast(baseCache); @@ -2457,14 +2459,8 @@ void Bars3DRenderer::updateAxisRange(QAbstract3DAxis::AxisOrientation orientatio { Abstract3DRenderer::updateAxisRange(orientation, min, max); - if (orientation == QAbstract3DAxis::AxisOrientationY) { - // Check if we have negative values - if (min < 0) - m_hasNegativeValues = true; - else if (min >= 0) - m_hasNegativeValues = false; + if (orientation == QAbstract3DAxis::AxisOrientationY) calculateHeightAdjustment(); - } } void Bars3DRenderer::updateAxisReversed(QAbstract3DAxis::AxisOrientation orientation, bool enable) @@ -2602,24 +2598,33 @@ void Bars3DRenderer::calculateSceneScalingFactors() void Bars3DRenderer::calculateHeightAdjustment() { + float min = m_axisCacheY.min(); + float max = m_axisCacheY.max(); GLfloat newAdjustment = 1.0f; - GLfloat maxAbs = qFabs(m_axisCacheY.max()); - - if (m_axisCacheY.max() < 0.0f) { - m_heightNormalizer = GLfloat(qFabs(m_axisCacheY.min()) - qFabs(m_axisCacheY.max())); - maxAbs = qFabs(m_axisCacheY.max()) - qFabs(m_axisCacheY.min()); + m_actualFloorLevel = qBound(min, m_floorLevel, max); + GLfloat maxAbs = qFabs(max - m_actualFloorLevel); + + // Check if we have negative values + if (min < m_actualFloorLevel) + m_hasNegativeValues = true; + else if (min >= m_actualFloorLevel) + m_hasNegativeValues = false; + + if (max < m_actualFloorLevel) { + m_heightNormalizer = GLfloat(qFabs(min) - qFabs(max)); + maxAbs = qFabs(max) - qFabs(min); } else { - m_heightNormalizer = GLfloat(m_axisCacheY.max() - m_axisCacheY.min()); + m_heightNormalizer = GLfloat(max - min); } // Height fractions are used in gradient calculations and are therefore doubled // Note that if max or min is exactly zero, we still consider it outside the range - if (m_axisCacheY.max() <= 0.0f || m_axisCacheY.min() >= 0.0f) { + if (max <= m_actualFloorLevel || min >= m_actualFloorLevel) { m_noZeroInRange = true; m_gradientFraction = 2.0f; } else { m_noZeroInRange = false; - GLfloat minAbs = qFabs(m_axisCacheY.min()); + GLfloat minAbs = qFabs(min - m_actualFloorLevel); m_gradientFraction = qMax(minAbs, maxAbs) / m_heightNormalizer * 2.0f; } @@ -2835,4 +2840,12 @@ void Bars3DRenderer::updateAspectRatio(float ratio) Q_UNUSED(ratio) } +void Bars3DRenderer::updateFloorLevel(float level) +{ + foreach (SeriesRenderCache *cache, m_renderCacheList) + cache->setDataDirty(true); + m_floorLevel = level; + calculateHeightAdjustment(); +} + QT_END_NAMESPACE_DATAVISUALIZATION diff --git a/src/datavisualization/engine/bars3drenderer_p.h b/src/datavisualization/engine/bars3drenderer_p.h index 47c9ab1d..92dae311 100644 --- a/src/datavisualization/engine/bars3drenderer_p.h +++ b/src/datavisualization/engine/bars3drenderer_p.h @@ -99,6 +99,8 @@ private: float m_zeroPosition; float m_xScaleFactor; float m_zScaleFactor; + float m_floorLevel; + float m_actualFloorLevel; public: explicit Bars3DRenderer(Bars3DController *controller); @@ -115,6 +117,7 @@ public: QVector3D convertPositionToTranslation(const QVector3D &position, bool isAbsolute); void updateAspectRatio(float ratio); + void updateFloorLevel(float level); protected: virtual void initializeOpenGL(); diff --git a/src/datavisualization/engine/q3dbars.cpp b/src/datavisualization/engine/q3dbars.cpp index bb7aca89..d42c11b7 100644 --- a/src/datavisualization/engine/q3dbars.cpp +++ b/src/datavisualization/engine/q3dbars.cpp @@ -330,6 +330,26 @@ QBar3DSeries *Q3DBars::selectedSeries() const return dptrc()->m_shared->selectedSeries(); } +/*! + * \property Q3DBars::floorLevel + * + * The desired floor level for the bar graph in Y-axis data coordinates. + * The actual floor level cannot go below Y-axis minimum or above Y-axis maximum. + * Defaults to zero. + */ +void Q3DBars::setFloorLevel(float level) +{ + if (level != floorLevel()) { + dptr()->m_shared->setFloorLevel(level); + emit floorLevelChanged(level); + } +} + +float Q3DBars::floorLevel() const +{ + return dptrc()->m_shared->floorLevel(); +} + /*! * Adds \a axis to the graph. The axes added via addAxis are not yet taken to use, * addAxis is simply used to give the ownership of the \a axis to the graph. diff --git a/src/datavisualization/engine/q3dbars.h b/src/datavisualization/engine/q3dbars.h index 7f9c981f..df1c846e 100644 --- a/src/datavisualization/engine/q3dbars.h +++ b/src/datavisualization/engine/q3dbars.h @@ -40,6 +40,7 @@ class QT_DATAVISUALIZATION_EXPORT Q3DBars : public QAbstract3DGraph Q_PROPERTY(QValue3DAxis *valueAxis READ valueAxis WRITE setValueAxis NOTIFY valueAxisChanged) Q_PROPERTY(QBar3DSeries *primarySeries READ primarySeries WRITE setPrimarySeries NOTIFY primarySeriesChanged) Q_PROPERTY(QBar3DSeries *selectedSeries READ selectedSeries NOTIFY selectedSeriesChanged) + Q_PROPERTY(float floorLevel READ floorLevel WRITE setFloorLevel NOTIFY floorLevelChanged) public: explicit Q3DBars(const QSurfaceFormat *format = 0, QWindow *parent = 0); @@ -75,6 +76,8 @@ public: QList axes() const; QBar3DSeries *selectedSeries() const; + void setFloorLevel(float level); + float floorLevel() const; signals: void multiSeriesUniformChanged(bool uniform); @@ -86,6 +89,7 @@ signals: void valueAxisChanged(QValue3DAxis *axis); void primarySeriesChanged(QBar3DSeries *series); void selectedSeriesChanged(QBar3DSeries *series); + void floorLevelChanged(float level); private: Q3DBarsPrivate *dptr(); diff --git a/src/datavisualizationqml2/datavisualizationqml2_plugin.cpp b/src/datavisualizationqml2/datavisualizationqml2_plugin.cpp index ecdd7454..a22fc55c 100644 --- a/src/datavisualizationqml2/datavisualizationqml2_plugin.cpp +++ b/src/datavisualizationqml2/datavisualizationqml2_plugin.cpp @@ -116,6 +116,7 @@ void QtDataVisualizationQml2Plugin::registerTypes(const char *uri) qmlRegisterType(uri, 1, 2, "Surface3D"); qmlRegisterType(uri, 1, 2, "Camera3D"); qmlRegisterType(uri, 1, 2, "Custom3DItem"); + qmlRegisterType(uri, 1, 2, "Bars3D"); // New types qmlRegisterType(uri, 1, 2, "InputHandler3D"); diff --git a/src/datavisualizationqml2/declarativebars.cpp b/src/datavisualizationqml2/declarativebars.cpp index 9670a7db..61e6aafb 100644 --- a/src/datavisualizationqml2/declarativebars.cpp +++ b/src/datavisualizationqml2/declarativebars.cpp @@ -129,6 +129,19 @@ QBar3DSeries *DeclarativeBars::selectedSeries() const return m_barsController->selectedSeries(); } +void DeclarativeBars::setFloorLevel(float level) +{ + if (level != floorLevel()) { + m_barsController->setFloorLevel(level); + emit floorLevelChanged(level); + } +} + +float DeclarativeBars::floorLevel() const +{ + return m_barsController->floorLevel(); +} + QQmlListProperty DeclarativeBars::seriesList() { return QQmlListProperty(this, this, diff --git a/src/datavisualizationqml2/declarativebars_p.h b/src/datavisualizationqml2/declarativebars_p.h index 52690813..05d66cdc 100644 --- a/src/datavisualizationqml2/declarativebars_p.h +++ b/src/datavisualizationqml2/declarativebars_p.h @@ -48,6 +48,7 @@ class DeclarativeBars : public AbstractDeclarative Q_PROPERTY(QQmlListProperty seriesList READ seriesList) Q_PROPERTY(QBar3DSeries *selectedSeries READ selectedSeries NOTIFY selectedSeriesChanged) Q_PROPERTY(QBar3DSeries *primarySeries READ primarySeries WRITE setPrimarySeries NOTIFY primarySeriesChanged) + Q_PROPERTY(float floorLevel READ floorLevel WRITE setFloorLevel NOTIFY floorLevelChanged REVISION 1) Q_CLASSINFO("DefaultProperty", "seriesList") public: @@ -85,6 +86,9 @@ public: QBar3DSeries *primarySeries() const; QBar3DSeries *selectedSeries() const; + void setFloorLevel(float level); + float floorLevel() const; + public slots: void handleAxisXChanged(QAbstract3DAxis *axis); void handleAxisYChanged(QAbstract3DAxis *axis); @@ -101,6 +105,7 @@ signals: void meshFileNameChanged(QString filename); void primarySeriesChanged(QBar3DSeries *series); void selectedSeriesChanged(QBar3DSeries *series); + Q_REVISION(1) void floorLevelChanged(float level); private: Bars3DController *m_barsController; diff --git a/tests/barstest/chart.cpp b/tests/barstest/chart.cpp index 137ce88e..9a81fc86 100644 --- a/tests/barstest/chart.cpp +++ b/tests/barstest/chart.cpp @@ -1519,6 +1519,12 @@ void GraphModifier::setCameraTargetZ(int value) qDebug() << "m_cameraTarget:" << m_cameraTarget; } +void GraphModifier::setFloorLevel(int value) +{ + m_graph->setFloorLevel(float(value)); + qDebug() << "Floor level:" << value; +} + void GraphModifier::populateFlatSeries(QBar3DSeries *series, int rows, int columns, float value) { QBarDataArray *dataArray = new QBarDataArray; diff --git a/tests/barstest/chart.h b/tests/barstest/chart.h index a5061df4..eac6d3f1 100644 --- a/tests/barstest/chart.h +++ b/tests/barstest/chart.h @@ -124,6 +124,7 @@ public slots: void setCameraTargetX(int value); void setCameraTargetY(int value); void setCameraTargetZ(int value); + void setFloorLevel(int value); signals: void shadowQualityChanged(int quality); diff --git a/tests/barstest/main.cpp b/tests/barstest/main.cpp index 409ecbab..1cc47859 100644 --- a/tests/barstest/main.cpp +++ b/tests/barstest/main.cpp @@ -262,6 +262,11 @@ int main(int argc, char **argv) reflectivitySlider->setValue(50); reflectivitySlider->setMaximum(100); + QSlider *floorLevelSlider = new QSlider(Qt::Horizontal, widget); + floorLevelSlider->setMinimum(-50); + floorLevelSlider->setValue(0); + floorLevelSlider->setMaximum(50); + QPushButton *toggleCustomItemButton = new QPushButton(widget); toggleCustomItemButton->setText(QStringLiteral("Toggle Custom Item")); @@ -435,7 +440,9 @@ int main(int argc, char **argv) vLayout3->addWidget(cameraTargetSliderZ, 0, Qt::AlignTop); vLayout3->addWidget(reflectionCheckBox, 0, Qt::AlignTop); vLayout3->addWidget(reflectivitySlider, 0, Qt::AlignTop); - vLayout3->addWidget(toggleCustomItemButton, 1, Qt::AlignTop); + vLayout3->addWidget(toggleCustomItemButton, 0, Qt::AlignTop); + vLayout3->addWidget(new QLabel(QStringLiteral("Adjust floor level")), 0, Qt::AlignTop); + vLayout3->addWidget(floorLevelSlider, 1, Qt::AlignTop); widget->show(); @@ -568,6 +575,8 @@ int main(int argc, char **argv) &GraphModifier::setReflection); QObject::connect(reflectivitySlider, &QSlider::valueChanged, modifier, &GraphModifier::setReflectivity); + QObject::connect(floorLevelSlider, &QSlider::valueChanged, modifier, + &GraphModifier::setFloorLevel); QObject::connect(toggleCustomItemButton, &QPushButton::clicked, modifier, &GraphModifier::toggleCustomItem); diff --git a/tests/qmlcamera/qml/qmlcamera/main.qml b/tests/qmlcamera/qml/qmlcamera/main.qml index cb22af59..be4feca3 100644 --- a/tests/qmlcamera/qml/qmlcamera/main.qml +++ b/tests/qmlcamera/qml/qmlcamera/main.qml @@ -74,6 +74,8 @@ Rectangle { customItemList: [shuttleItem, labelItem] orthoProjection: true + + floorLevel: 10 } Custom3DItem { -- cgit v1.2.3 From 0ab45b018fa98e9ef6cffd70178f208eb4f16550 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Thu, 25 Sep 2014 15:04:44 +0300 Subject: Implement zooming to cursor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Zooming to cursor is now default operating mode of the default input handler. Task-number: QTRD-3263 Change-Id: I5699fc0ce7393059538972cd52f31f06d87e3d8d Reviewed-by: Tomi Korpipää --- .../datavisualization/volumetric/volumetric.cpp | 4 + ...tdatavisualization-qml-abstractdeclarative.qdoc | 22 ++++ .../engine/abstract3dcontroller.cpp | 33 ++++-- .../engine/abstract3dcontroller_p.h | 7 +- .../engine/abstract3drenderer.cpp | 124 +++++++++++++++++++-- .../engine/abstract3drenderer_p.h | 19 +++- src/datavisualization/engine/bars3drenderer.cpp | 20 +++- src/datavisualization/engine/engine.qrc | 3 +- src/datavisualization/engine/q3dscene.cpp | 62 ++++++++++- src/datavisualization/engine/q3dscene.h | 5 + src/datavisualization/engine/q3dscene_p.h | 3 + src/datavisualization/engine/qabstract3dgraph.cpp | 27 +++++ src/datavisualization/engine/qabstract3dgraph.h | 4 + src/datavisualization/engine/scatter3drenderer.cpp | 10 +- .../engine/shaders/colorandposition.vert | 10 -- src/datavisualization/engine/shaders/position.vert | 10 ++ .../engine/shaders/positionmap.frag | 11 ++ src/datavisualization/engine/surface3drenderer.cpp | 17 ++- src/datavisualization/input/q3dinputhandler.cpp | 122 +++++++++++++++++++- src/datavisualization/input/q3dinputhandler.h | 4 + src/datavisualization/input/q3dinputhandler_p.h | 19 +++- .../input/qtouch3dinputhandler.cpp | 22 +++- .../input/qtouch3dinputhandler_p.h | 9 +- src/datavisualization/utils/texturehelper.cpp | 25 +++++ src/datavisualization/utils/texturehelper_p.h | 2 + src/datavisualizationqml2/abstractdeclarative.cpp | 7 ++ src/datavisualizationqml2/abstractdeclarative_p.h | 4 + .../datavisualizationqml2_plugin.cpp | 2 + tests/barstest/chart.cpp | 6 + tests/barstest/chart.h | 1 + tests/barstest/main.cpp | 7 ++ tests/scattertest/scatterchart.cpp | 6 + tests/surfacetest/graphmodifier.cpp | 25 ++++- tests/surfacetest/graphmodifier.h | 5 +- tests/surfacetest/main.cpp | 2 +- tests/volumetrictest/volumetrictest.pro | 4 - 36 files changed, 593 insertions(+), 70 deletions(-) delete mode 100644 src/datavisualization/engine/shaders/colorandposition.vert create mode 100644 src/datavisualization/engine/shaders/position.vert create mode 100644 src/datavisualization/engine/shaders/positionmap.frag diff --git a/examples/datavisualization/volumetric/volumetric.cpp b/examples/datavisualization/volumetric/volumetric.cpp index cbbaf2a1..65599c31 100644 --- a/examples/datavisualization/volumetric/volumetric.cpp +++ b/examples/datavisualization/volumetric/volumetric.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -86,6 +87,9 @@ VolumetricModifier::VolumetricModifier(Q3DScatter *scatter) //! [6] m_graph->activeTheme()->setBackgroundEnabled(false); + // Only allow zooming at the center to avoid clipping issues + static_cast(m_graph->activeInputHandler())->setZoomAtTargetEnabled(false); + toggleAreaAll(true); #if !defined(QT_OPENGL_ES_2) diff --git a/src/datavisualization/doc/src/qtdatavisualization-qml-abstractdeclarative.qdoc b/src/datavisualization/doc/src/qtdatavisualization-qml-abstractdeclarative.qdoc index 5f06d404..3dc5beea 100644 --- a/src/datavisualization/doc/src/qtdatavisualization-qml-abstractdeclarative.qdoc +++ b/src/datavisualization/doc/src/qtdatavisualization-qml-abstractdeclarative.qdoc @@ -385,3 +385,25 @@ * * \sa ValueAxis3D::labelFormat */ + +/*! + * \qmlproperty vector3d AbstractGraph3D::queriedGraphPosition + * \since QtDataVisualization 1.2 + * + * This read-only property contains the latest graph position values along each axis queried using + * Scene3D::graphPositionQuery. The values are normalized to range \c{[-1, 1]}. + * If the queried position was outside the graph bounds, the values + * will not reflect the real position, but will instead be some undefined position outside + * the range \c{[-1, 1]}. The value will be undefined before any queries are made. + * + * There isn't a single correct 3D coordinate to match to each specific screen position, so to be + * consistent, the queries are always done against the inner sides of an invisible box surrounding + * the graph. + * + * \note Bar graphs only allow querying graph position at the graph floor level, + * so the Y-value is always zero for bar graphs and the valid queries can be only made at + * screen positions that contain the floor of the graph. + * + * \sa Scene3D::graphPositionQuery + */ + diff --git a/src/datavisualization/engine/abstract3dcontroller.cpp b/src/datavisualization/engine/abstract3dcontroller.cpp index e2f8c1ad..c1d27bab 100644 --- a/src/datavisualization/engine/abstract3dcontroller.cpp +++ b/src/datavisualization/engine/abstract3dcontroller.cpp @@ -62,6 +62,7 @@ Abstract3DController::Abstract3DController(QRect initialViewport, Q3DScene *scen { if (!m_scene) m_scene = new Q3DScene; + m_scene->setParent(this); // Set initial theme Q3DTheme *defaultTheme = new Q3DTheme(Q3DTheme::ThemeQt); @@ -165,7 +166,12 @@ void Abstract3DController::synchDataToRenderer() { // Subclass implementations check for renderer validity already, so no need to check here. - // If there is a pending click from renderer, handle that first. + // If there are pending queries, handle those first + if (m_renderer->isGraphPositionQueryPending()) { + handlePendingGraphPositionQuery(); + m_renderer->clearGraphPositionQueryPending(); + } + if (m_renderer->isClickPending()) { handlePendingClick(); m_renderer->clearClickPending(); @@ -804,14 +810,15 @@ void Abstract3DController::setActiveInputHandler(QAbstract3DInputHandler *inputH addInputHandler(inputHandler); m_activeInputHandler = inputHandler; - if (m_activeInputHandler) + if (m_activeInputHandler) { m_activeInputHandler->setScene(m_scene); - // Connect the input handler - QObject::connect(m_activeInputHandler, &QAbstract3DInputHandler::inputViewChanged, this, - &Abstract3DController::handleInputViewChanged); - QObject::connect(m_activeInputHandler, &QAbstract3DInputHandler::positionChanged, this, - &Abstract3DController::handleInputPositionChanged); + // Connect the input handler + QObject::connect(m_activeInputHandler, &QAbstract3DInputHandler::inputViewChanged, this, + &Abstract3DController::handleInputViewChanged); + QObject::connect(m_activeInputHandler, &QAbstract3DInputHandler::positionChanged, this, + &Abstract3DController::handleInputPositionChanged); + } // Notify change of input handler emit activeInputHandlerChanged(m_activeInputHandler); @@ -1468,6 +1475,12 @@ void Abstract3DController::handlePendingClick() emit elementSelected(type); } +void Abstract3DController::handlePendingGraphPositionQuery() +{ + m_queriedGraphPosition = m_renderer->queriedGraphPosition(); + emit queriedGraphPositionChanged(m_queriedGraphPosition); +} + int Abstract3DController::selectedLabelIndex() const { int index = m_renderer->m_selectedLabelIndex; @@ -1659,4 +1672,10 @@ QLocale Abstract3DController::locale() const return m_locale; } +QVector3D Abstract3DController::queriedGraphPosition() const +{ + return m_queriedGraphPosition; +} + + QT_END_NAMESPACE_DATAVISUALIZATION diff --git a/src/datavisualization/engine/abstract3dcontroller_p.h b/src/datavisualization/engine/abstract3dcontroller_p.h index 03061a13..125e9885 100644 --- a/src/datavisualization/engine/abstract3dcontroller_p.h +++ b/src/datavisualization/engine/abstract3dcontroller_p.h @@ -173,6 +173,7 @@ private: bool m_reflectionEnabled; qreal m_reflectivity; QLocale m_locale; + QVector3D m_queriedGraphPosition; protected: Q3DScene *m_scene; @@ -302,6 +303,8 @@ public: void setLocale(const QLocale &locale); QLocale locale() const; + QVector3D queriedGraphPosition() const; + void emitNeedRender(); virtual void clearSelection() = 0; @@ -327,7 +330,8 @@ public: virtual void handleAxisTitleVisibilityChangedBySender(QObject *sender); virtual void handleAxisTitleFixedChangedBySender(QObject *sender); virtual void handleSeriesVisibilityChangedBySender(QObject *sender); - virtual void handlePendingClick() = 0; + virtual void handlePendingClick(); + virtual void handlePendingGraphPositionQuery(); virtual void adjustAxisRanges() = 0; void markSeriesItemLabelsDirty(); @@ -384,6 +388,7 @@ signals: void reflectionChanged(bool enabled); void reflectivityChanged(qreal reflectivity); void localeChanged(const QLocale &locale); + void queriedGraphPositionChanged(const QVector3D &data); protected: virtual QAbstract3DAxis *createDefaultAxis(QAbstract3DAxis::AxisOrientation orientation); diff --git a/src/datavisualization/engine/abstract3drenderer.cpp b/src/datavisualization/engine/abstract3drenderer.cpp index d0f3c04b..a5e1bc9b 100644 --- a/src/datavisualization/engine/abstract3drenderer.cpp +++ b/src/datavisualization/engine/abstract3drenderer.cpp @@ -54,8 +54,11 @@ Abstract3DRenderer::Abstract3DRenderer(Abstract3DController *controller) m_devicePixelRatio(1.0f), m_selectionLabelDirty(true), m_clickPending(false), + m_graphPositionQueryPending(false), m_clickedSeries(0), m_clickedType(QAbstract3DGraph::ElementNone), + m_selectedLabelIndex(-1), + m_selectedCustomItemIndex(-1), m_selectionLabelItem(0), m_visibleSeriesCount(0), m_customItemShader(0), @@ -64,6 +67,9 @@ Abstract3DRenderer::Abstract3DRenderer(Abstract3DController *controller) m_volumeTextureSliceShader(0), m_volumeSliceFrameShader(0), m_labelShader(0), + m_cursorPositionShader(0), + m_cursorPositionFrameBuffer(0), + m_cursorPositionTexture(0), m_useOrthoProjection(false), m_xFlipped(false), m_yFlipped(false), @@ -72,6 +78,7 @@ Abstract3DRenderer::Abstract3DRenderer(Abstract3DController *controller) m_backgroundObj(0), m_gridLineObj(0), m_labelObj(0), + m_positionMapperObj(0), m_graphAspectRatio(2.0f), m_graphHorizontalAspectRatio(0.0f), m_polarGraph(false), @@ -110,6 +117,7 @@ Abstract3DRenderer::~Abstract3DRenderer() delete m_volumeSliceFrameShader; delete m_volumeTextureSliceShader; delete m_labelShader; + delete m_cursorPositionShader; foreach (SeriesRenderCache *cache, m_renderCacheList) { cache->cleanup(m_textureHelper); @@ -127,9 +135,15 @@ Abstract3DRenderer::~Abstract3DRenderer() ObjectHelper::releaseObjectHelper(this, m_backgroundObj); ObjectHelper::releaseObjectHelper(this, m_gridLineObj); ObjectHelper::releaseObjectHelper(this, m_labelObj); + ObjectHelper::releaseObjectHelper(this, m_positionMapperObj); if (m_textureHelper) { m_textureHelper->deleteTexture(&m_depthTexture); + m_textureHelper->deleteTexture(&m_cursorPositionTexture); + + if (QOpenGLContext::currentContext()) + m_textureHelper->glDeleteFramebuffers(1, &m_cursorPositionFrameBuffer); + delete m_textureHelper; } } @@ -157,6 +171,12 @@ void Abstract3DRenderer::initializeOpenGL() initLabelShaders(QStringLiteral(":/shaders/vertexLabel"), QStringLiteral(":/shaders/fragmentLabel")); + + initCursorPositionShaders(QStringLiteral(":/shaders/vertexPosition"), + QStringLiteral(":/shaders/fragmentPositionMap")); + + loadLabelMesh(); + loadPositionMapperMesh(); } void Abstract3DRenderer::render(const GLuint defaultFboHandle) @@ -191,11 +211,6 @@ void Abstract3DRenderer::updateSelectionState(SelectionState state) m_selectionState = state; } -void Abstract3DRenderer::updateInputPosition(const QPoint &position) -{ - m_inputPosition = position; -} - void Abstract3DRenderer::initGradientShaders(const QString &vertexShader, const QString &fragmentShader) { @@ -244,6 +259,27 @@ void Abstract3DRenderer::initLabelShaders(const QString &vertexShader, const QSt m_labelShader->initialize(); } +void Abstract3DRenderer::initCursorPositionShaders(const QString &vertexShader, + const QString &fragmentShader) +{ + // Init the shader + delete m_cursorPositionShader; + m_cursorPositionShader = new ShaderHelper(this, vertexShader, fragmentShader); + m_cursorPositionShader->initialize(); +} + +void Abstract3DRenderer::initCursorPositionBuffer() +{ + m_textureHelper->deleteTexture(&m_cursorPositionTexture); + + if (m_primarySubViewport.size().isEmpty()) + return; + + m_cursorPositionTexture = + m_textureHelper->createCursorPositionTexture(m_primarySubViewport.size(), + m_cursorPositionFrameBuffer); +} + void Abstract3DRenderer::updateTheme(Q3DTheme *theme) { // Synchronize the controller theme with renderer @@ -270,8 +306,12 @@ void Abstract3DRenderer::updateScene(Q3DScene *scene) } QPoint logicalPixelPosition = scene->selectionQueryPosition(); - updateInputPosition(QPoint(logicalPixelPosition.x() * m_devicePixelRatio, - logicalPixelPosition.y() * m_devicePixelRatio)); + m_inputPosition = QPoint(logicalPixelPosition.x() * m_devicePixelRatio, + logicalPixelPosition.y() * m_devicePixelRatio); + + QPoint logicalGraphPosition = scene->graphPositionQuery(); + m_graphPositionQuery = QPoint(logicalGraphPosition.x() * m_devicePixelRatio, + logicalGraphPosition.y() * m_devicePixelRatio); // Synchronize the renderer scene to controller scene scene->d_ptr->sync(*m_cachedScene->d_ptr); @@ -296,6 +336,11 @@ void Abstract3DRenderer::updateScene(Q3DScene *scene) updateSelectionState(SelectOnScene); } } + + if (Q3DScene::invalidSelectionPoint() != logicalGraphPosition) { + m_graphPositionQueryPending = true; + scene->setGraphPositionQuery(Q3DScene::invalidSelectionPoint()); + } } void Abstract3DRenderer::updateTextures() @@ -347,7 +392,7 @@ void Abstract3DRenderer::reInitShaders() QStringLiteral(":/shaders/fragmentTexture3D"), QStringLiteral(":/shaders/fragmentTexture3DLowDef"), QStringLiteral(":/shaders/fragmentTexture3DSlice"), - QStringLiteral(":/shaders/colorAndPosition"), + QStringLiteral(":/shaders/vertexPosition"), QStringLiteral(":/shaders/fragment3DSliceFrames")); #else if (m_cachedOptimizationHint.testFlag(QAbstract3DGraph::OptimizationStatic) @@ -434,6 +479,8 @@ void Abstract3DRenderer::handleResize() // Re-init depth buffer updateDepthBuffer(); #endif + + initCursorPositionBuffer(); } void Abstract3DRenderer::calculateZoomLevel() @@ -929,6 +976,12 @@ void Abstract3DRenderer::loadLabelMesh() QStringLiteral(":/defaultMeshes/plane")); } +void Abstract3DRenderer::loadPositionMapperMesh() +{ + ObjectHelper::resetObjectHelper(this, m_positionMapperObj, + QStringLiteral(":/defaultMeshes/barFull")); +} + void Abstract3DRenderer::generateBaseColorTexture(const QColor &color, GLuint *texture) { m_textureHelper->deleteTexture(texture); @@ -1305,7 +1358,7 @@ void Abstract3DRenderer::drawCustomItems(RenderingState state, // Check that the render item is visible, and skip drawing if not // Also check if reflected item is on the "wrong" side, and skip drawing if it is if (!item->isVisible() || ((m_reflectionEnabled && reflection < 0.0f) - && (m_yFlipped == item->translation().y() >= 0.0))) { + && (m_yFlipped == (item->translation().y() >= 0.0)))) { continue; } if (loopCount == 0) { @@ -1627,6 +1680,59 @@ void Abstract3DRenderer::drawVolumeSliceFrame(const CustomRenderItem *item, Qt:: } +void Abstract3DRenderer::queriedGraphPosition(const QMatrix4x4 &projectionViewMatrix, + const QVector3D &scaling, + GLuint defaultFboHandle) +{ + m_cursorPositionShader->bind(); + + // Set up mapper framebuffer + glBindFramebuffer(GL_FRAMEBUFFER, m_cursorPositionFrameBuffer); + glViewport(0, 0, + m_primarySubViewport.width(), + m_primarySubViewport.height()); + glClearColor(1.0f, 1.0f, 1.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + glDisable(GL_DITHER); // Dither may affect colors if enabled + glEnable(GL_CULL_FACE); + glCullFace(GL_FRONT); + + // Draw a cube scaled to the graph dimensions + QMatrix4x4 modelMatrix; + QMatrix4x4 MVPMatrix; + + modelMatrix.scale(scaling); + + MVPMatrix = projectionViewMatrix * modelMatrix; + m_cursorPositionShader->setUniformValue(m_cursorPositionShader->MVP(), MVPMatrix); + m_drawer->drawObject(m_cursorPositionShader, m_positionMapperObj); + + QVector4D dataColor = Utils::getSelection(m_graphPositionQuery, + m_primarySubViewport.height()); + if (dataColor.w() > 0.0f) { + // If position is outside the graph, set the position well outside the graph boundaries + dataColor = QVector4D(-10000.0f, -10000.0f, -10000.0f, 0.0f); + } else { + // Normalize to range [0.0, 1.0] + dataColor /= 255.0f; + } + + // Restore state + glEnable(GL_DITHER); + glCullFace(GL_BACK); + glBindFramebuffer(GL_FRAMEBUFFER, defaultFboHandle); + glViewport(m_primarySubViewport.x(), + m_primarySubViewport.y(), + m_primarySubViewport.width(), + m_primarySubViewport.height()); + + QVector3D normalizedValues = dataColor.toVector3D() * 2.0f; + normalizedValues -= oneVector; + m_queriedGraphPosition = QVector3D(normalizedValues.x(), + normalizedValues.y(), + normalizedValues.z()); +} + void Abstract3DRenderer::calculatePolarXZ(const QVector3D &dataPos, float &x, float &z) const { // x is angular, z is radial diff --git a/src/datavisualization/engine/abstract3drenderer_p.h b/src/datavisualization/engine/abstract3drenderer_p.h index a46e6176..6d134ee3 100644 --- a/src/datavisualization/engine/abstract3drenderer_p.h +++ b/src/datavisualization/engine/abstract3drenderer_p.h @@ -80,7 +80,6 @@ public: virtual void updateTextures(); virtual void initSelectionBuffer() = 0; virtual void updateSelectionState(SelectionState state); - virtual void updateInputPosition(const QPoint &position); #if !defined(QT_OPENGL_ES_2) virtual void updateDepthBuffer() = 0; @@ -99,6 +98,9 @@ public: const QString &sliceFrameVertexShader, const QString &sliceFrameShader); virtual void initLabelShaders(const QString &vertexShader, const QString &fragmentShader); + virtual void initCursorPositionShaders(const QString &vertexShader, + const QString &fragmentShader); + virtual void initCursorPositionBuffer(); virtual void updateAxisType(QAbstract3DAxis::AxisOrientation orientation, QAbstract3DAxis::AxisType type); @@ -120,7 +122,7 @@ public: virtual void updateAxisLabelAutoRotation(QAbstract3DAxis::AxisOrientation orientation, float angle); virtual void updateAxisTitleVisibility(QAbstract3DAxis::AxisOrientation orientation, - bool visible); + bool visible); virtual void updateAxisTitleFixed(QAbstract3DAxis::AxisOrientation orientation, bool fixed); virtual void modifiedSeriesList(const QVector &seriesList); @@ -145,6 +147,9 @@ public: inline void clearClickPending() { m_clickPending = false; } inline QAbstract3DSeries *clickedSeries() const { return m_clickedSeries; } inline QAbstract3DGraph::ElementType clickedType() { return m_clickedType; } + inline bool isGraphPositionQueryPending() { return m_graphPositionQueryPending; } + inline void clearGraphPositionQueryPending() { m_graphPositionQueryPending = false; } + inline QVector3D queriedGraphPosition() const { return m_queriedGraphPosition; } LabelItem &selectionLabelItem(); void setSelectionLabel(const QString &label); @@ -199,6 +204,7 @@ protected: void loadGridLineMesh(); void loadLabelMesh(); + void loadPositionMapperMesh(); void drawRadialGrid(ShaderHelper *shader, float yFloorLinePos, const QMatrix4x4 &projectionViewMatrix, const QMatrix4x4 &depthMatrix); @@ -213,6 +219,8 @@ protected: virtual void getVisibleItemBounds(QVector3D &minBounds, QVector3D &maxBounds) = 0; void drawVolumeSliceFrame(const CustomRenderItem *item, Qt::Axis axis, const QMatrix4x4 &projectionViewMatrix); + void queriedGraphPosition(const QMatrix4x4 &projectionViewMatrix, const QVector3D &scaling, + GLuint defaultFboHandle); bool m_hasNegativeValues; Q3DTheme *m_cachedTheme; @@ -241,10 +249,13 @@ protected: float m_devicePixelRatio; bool m_selectionLabelDirty; bool m_clickPending; + bool m_graphPositionQueryPending; QAbstract3DSeries *m_clickedSeries; QAbstract3DGraph::ElementType m_clickedType; int m_selectedLabelIndex; int m_selectedCustomItemIndex; + QVector3D m_queriedGraphPosition; + QPoint m_graphPositionQuery; QString m_selectionLabel; LabelItem *m_selectionLabelItem; @@ -256,6 +267,9 @@ protected: ShaderHelper *m_volumeTextureSliceShader; ShaderHelper *m_volumeSliceFrameShader; ShaderHelper *m_labelShader; + ShaderHelper *m_cursorPositionShader; + GLuint m_cursorPositionFrameBuffer; + GLuint m_cursorPositionTexture; bool m_useOrthoProjection; bool m_xFlipped; @@ -266,6 +280,7 @@ protected: ObjectHelper *m_backgroundObj; // Shared reference ObjectHelper *m_gridLineObj; // Shared reference ObjectHelper *m_labelObj; // Shared reference + ObjectHelper *m_positionMapperObj; // Shared reference float m_graphAspectRatio; float m_graphHorizontalAspectRatio; diff --git a/src/datavisualization/engine/bars3drenderer.cpp b/src/datavisualization/engine/bars3drenderer.cpp index 9f3c2e2a..2d394d57 100644 --- a/src/datavisualization/engine/bars3drenderer.cpp +++ b/src/datavisualization/engine/bars3drenderer.cpp @@ -122,9 +122,6 @@ void Bars3DRenderer::initializeOpenGL() // Load grid line mesh loadGridLineMesh(); - // Load label mesh - loadLabelMesh(); - // Load background mesh (we need to be initialized first) loadBackgroundMesh(); } @@ -1161,6 +1158,16 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) } #endif + // Do position mapping when necessary + if (m_graphPositionQueryPending) { + QVector3D graphDimensions(m_xScaleFactor, 0.0f, m_zScaleFactor); + queriedGraphPosition(projectionViewMatrix, graphDimensions, defaultFboHandle); + + // Y is always at floor level + m_queriedGraphPosition.setY(0.0f); + emit needRender(); + } + // Skip selection mode drawing if we're slicing or have no selection mode if (!m_cachedIsSlicingActivated && m_cachedSelectionMode > QAbstract3DGraph::SelectionNone && m_selectionState == SelectOnScene @@ -2732,8 +2739,11 @@ void Bars3DRenderer::updateSlicingActive(bool isSlicing) m_cachedIsSlicingActivated = isSlicing; - if (!m_cachedIsSlicingActivated) - initSelectionBuffer(); // We need to re-init selection buffer in case there has been a resize + if (!m_cachedIsSlicingActivated) { + // We need to re-init selection buffer in case there has been a resize + initSelectionBuffer(); + initCursorPositionBuffer(); + } #if !defined(QT_OPENGL_ES_2) updateDepthBuffer(); // Re-init depth buffer as well diff --git a/src/datavisualization/engine/engine.qrc b/src/datavisualization/engine/engine.qrc index af759ba3..8c234a3b 100644 --- a/src/datavisualization/engine/engine.qrc +++ b/src/datavisualization/engine/engine.qrc @@ -67,6 +67,7 @@ shaders/texture3dlowdef.frag shaders/point_ES2_UV.vert shaders/3dsliceframes.frag - shaders/colorandposition.vert + shaders/position.vert + shaders/positionmap.frag diff --git a/src/datavisualization/engine/q3dscene.cpp b/src/datavisualization/engine/q3dscene.cpp index 08721b97..87826a8b 100644 --- a/src/datavisualization/engine/q3dscene.cpp +++ b/src/datavisualization/engine/q3dscene.cpp @@ -95,6 +95,26 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION * After the rendering pass the property is returned to its default state of invalidSelectionPoint. */ +/*! + * \qmlproperty point Scene3D::graphPositionQuery + * + * This property contains the coordinates for the user input that should be processed + * by the scene as a graph position query. If this is set to value other than + * invalidSelectionPoint, the graph tries to match a graph position to the given \a point + * within the primary viewport. + * After the rendering pass this property is returned to its default state of + * invalidSelectionPoint. The queried graph position can be read from + * AbstractGraph3D::queriedGraphPosition property after the next render pass. + * + * There isn't a single correct 3D coordinate to match to each specific screen position, so to be + * consistent, the queries are always done against the inner sides of an invisible box surrounding + * the graph. + * + * \note Bar graphs allow graph position queries only at the graph floor level. + * + * \sa AbstractGraph3D::queriedGraphPosition + */ + /*! * \qmlproperty bool Scene3D::slicingActive * @@ -258,7 +278,7 @@ void Q3DScene::setSecondarySubViewport(const QRect &secondarySubViewport) * \property Q3DScene::selectionQueryPosition * * This property contains the coordinates for the user input that should be processed - * by the scene as selection. If this is set to value other than invalidSelectionPoint() the + * by the scene as a selection. If this is set to value other than invalidSelectionPoint(), the * graph tries to select a data item, axis label, or a custom item at the given \a point within * the primary viewport. * After the rendering pass the property is returned to its default state of @@ -292,6 +312,42 @@ QPoint Q3DScene::invalidSelectionPoint() return invalidSelectionPos; } +/*! + * \property Q3DScene::graphPositionQuery + * + * This property contains the coordinates for the user input that should be processed + * by the scene as a graph position query. If this is set to value other than + * invalidSelectionPoint(), the graph tries to match a graph position to the given \a point + * within the primary viewport. + * After the rendering pass this property is returned to its default state of + * invalidSelectionPoint(). The queried graph position can be read from + * QAbstract3DGraph::queriedGraphPosition property after the next render pass. + * + * There isn't a single correct 3D coordinate to match to each specific screen position, so to be + * consistent, the queries are always done against the inner sides of an invisible box surrounding + * the graph. + * + * \note Bar graphs allow graph position queries only at the graph floor level. + * + * \sa QAbstract3DGraph::queriedGraphPosition + */ +void Q3DScene::setGraphPositionQuery(const QPoint &point) +{ + if (point != d_ptr->m_graphPositionQueryPosition) { + d_ptr->m_graphPositionQueryPosition = point; + d_ptr->m_changeTracker.graphPositionQueryPositionChanged = true; + d_ptr->m_sceneDirty = true; + + emit graphPositionQueryChanged(point); + emit d_ptr->needRender(); + } +} + +QPoint Q3DScene::graphPositionQuery() const +{ + return d_ptr->m_graphPositionQueryPosition; +} + /*! * \property Q3DScene::slicingActive * @@ -453,7 +509,9 @@ Q3DScenePrivate::Q3DScenePrivate(Q3DScene *q) : m_light(), m_isUnderSideCameraEnabled(false), m_isSlicingActive(false), - m_selectionQueryPosition(Q3DScene::invalidSelectionPoint()) + m_selectionQueryPosition(Q3DScene::invalidSelectionPoint()), + m_graphPositionQueryPosition(Q3DScene::invalidSelectionPoint()), + m_sceneDirty(true) { } diff --git a/src/datavisualization/engine/q3dscene.h b/src/datavisualization/engine/q3dscene.h index 1699b125..a46b4d7b 100644 --- a/src/datavisualization/engine/q3dscene.h +++ b/src/datavisualization/engine/q3dscene.h @@ -41,6 +41,7 @@ class QT_DATAVISUALIZATION_EXPORT Q3DScene : public QObject Q_PROPERTY(Q3DCamera* activeCamera READ activeCamera WRITE setActiveCamera NOTIFY activeCameraChanged) Q_PROPERTY(Q3DLight* activeLight READ activeLight WRITE setActiveLight NOTIFY activeLightChanged) Q_PROPERTY(float devicePixelRatio READ devicePixelRatio WRITE setDevicePixelRatio NOTIFY devicePixelRatioChanged) + Q_PROPERTY(QPoint graphPositionQuery READ graphPositionQuery WRITE setGraphPositionQuery NOTIFY graphPositionQueryChanged REVISION 1) public: Q3DScene(QObject *parent = 0); @@ -60,6 +61,9 @@ public: QPoint selectionQueryPosition() const; static QPoint invalidSelectionPoint(); + void setGraphPositionQuery(const QPoint &point); + QPoint graphPositionQuery() const; + void setSlicingActive(bool isSlicing); bool isSlicingActive() const; @@ -85,6 +89,7 @@ signals: void activeLightChanged(Q3DLight *light); void devicePixelRatioChanged(float pixelRatio); void selectionQueryPositionChanged(const QPoint &position); + Q_REVISION(1) void graphPositionQueryChanged(const QPoint &position); private: QScopedPointer d_ptr; diff --git a/src/datavisualization/engine/q3dscene_p.h b/src/datavisualization/engine/q3dscene_p.h index c86a0ec1..b7321c25 100644 --- a/src/datavisualization/engine/q3dscene_p.h +++ b/src/datavisualization/engine/q3dscene_p.h @@ -47,6 +47,7 @@ struct Q3DSceneChangeBitField { bool slicingActivatedChanged : 1; bool devicePixelRatioChanged : 1; bool selectionQueryPositionChanged : 1; + bool graphPositionQueryPositionChanged : 1; bool windowSizeChanged : 1; Q3DSceneChangeBitField() @@ -59,6 +60,7 @@ struct Q3DSceneChangeBitField { slicingActivatedChanged(true), devicePixelRatioChanged(true), selectionQueryPositionChanged(false), + graphPositionQueryPositionChanged(false), windowSizeChanged(true) { } @@ -108,6 +110,7 @@ public: bool m_isUnderSideCameraEnabled; bool m_isSlicingActive; QPoint m_selectionQueryPosition; + QPoint m_graphPositionQueryPosition; QSize m_windowSize; QRect m_glViewport; QRect m_glPrimarySubViewport; diff --git a/src/datavisualization/engine/qabstract3dgraph.cpp b/src/datavisualization/engine/qabstract3dgraph.cpp index 8ced09fe..0249478f 100644 --- a/src/datavisualization/engine/qabstract3dgraph.cpp +++ b/src/datavisualization/engine/qabstract3dgraph.cpp @@ -785,6 +785,31 @@ QLocale QAbstract3DGraph::locale() const return d_ptr->m_visualController->locale(); } +/*! + * \property QAbstract3DGraph::queriedGraphPosition + * \since QtDataVisualization 1.2 + * + * This read-only property contains the latest graph position values along each axis queried using + * Q3DScene::graphPositionQuery. The values are normalized to range \c{[-1, 1]}. + * If the queried position was outside the graph bounds, the values + * will not reflect the real position, but will instead be some undefined position outside + * the range \c{[-1, 1]}. The value will be undefined before any queries are made. + * + * There isn't a single correct 3D coordinate to match to each specific screen position, so to be + * consistent, the queries are always done against the inner sides of an invisible box surrounding + * the graph. + * + * \note Bar graphs only allow querying graph position at the graph floor level, + * so the Y-value is always zero for bar graphs and the valid queries can be only made at + * screen positions that contain the floor of the graph. + * + * \sa Q3DScene::graphPositionQuery + */ +QVector3D QAbstract3DGraph::queriedGraphPosition() const +{ + return d_ptr->m_visualController->queriedGraphPosition(); +} + /*! * \internal */ @@ -948,6 +973,8 @@ void QAbstract3DGraphPrivate::setVisualController(Abstract3DController *controll &QAbstract3DGraph::reflectivityChanged); QObject::connect(m_visualController, &Abstract3DController::localeChanged, q_ptr, &QAbstract3DGraph::localeChanged); + QObject::connect(m_visualController, &Abstract3DController::queriedGraphPositionChanged, q_ptr, + &QAbstract3DGraph::queriedGraphPositionChanged); } void QAbstract3DGraphPrivate::handleDevicePixelRatioChange() diff --git a/src/datavisualization/engine/qabstract3dgraph.h b/src/datavisualization/engine/qabstract3dgraph.h index 2d76f780..4a6de4f2 100644 --- a/src/datavisualization/engine/qabstract3dgraph.h +++ b/src/datavisualization/engine/qabstract3dgraph.h @@ -57,6 +57,7 @@ class QT_DATAVISUALIZATION_EXPORT QAbstract3DGraph : public QWindow, protected Q Q_PROPERTY(bool reflection READ isReflection WRITE setReflection NOTIFY reflectionChanged) Q_PROPERTY(qreal reflectivity READ reflectivity WRITE setReflectivity NOTIFY reflectivityChanged) Q_PROPERTY(QLocale locale READ locale WRITE setLocale NOTIFY localeChanged) + Q_PROPERTY(QVector3D queriedGraphPosition READ queriedGraphPosition NOTIFY queriedGraphPositionChanged) protected: explicit QAbstract3DGraph(QAbstract3DGraphPrivate *d, const QSurfaceFormat *format, @@ -175,6 +176,8 @@ public: void setLocale(const QLocale &locale); QLocale locale() const; + QVector3D queriedGraphPosition() const; + protected: bool event(QEvent *event); void resizeEvent(QResizeEvent *event); @@ -204,6 +207,7 @@ signals: void reflectionChanged(bool enabled); void reflectivityChanged(qreal reflectivity); void localeChanged(const QLocale &locale); + void queriedGraphPositionChanged(const QVector3D &data); private: Q_DISABLE_COPY(QAbstract3DGraph) diff --git a/src/datavisualization/engine/scatter3drenderer.cpp b/src/datavisualization/engine/scatter3drenderer.cpp index e9e395be..d1dca8c6 100644 --- a/src/datavisualization/engine/scatter3drenderer.cpp +++ b/src/datavisualization/engine/scatter3drenderer.cpp @@ -117,9 +117,6 @@ void Scatter3DRenderer::initializeOpenGL() loadGridLineMesh(); #endif - // Load label mesh - loadLabelMesh(); - // Set view port glViewport(m_primarySubViewport.x(), m_primarySubViewport.y(), @@ -651,6 +648,13 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) #endif ShaderHelper *selectionShader = m_selectionShader; + // Do position mapping when necessary + if (m_graphPositionQueryPending) { + QVector3D graphDimensions(m_scaleX, m_scaleY, m_scaleZ); + queriedGraphPosition(projectionViewMatrix, graphDimensions, defaultFboHandle); + emit needRender(); + } + // Skip selection mode drawing if we have no selection mode if (m_cachedSelectionMode > QAbstract3DGraph::SelectionNone && SelectOnScene == m_selectionState diff --git a/src/datavisualization/engine/shaders/colorandposition.vert b/src/datavisualization/engine/shaders/colorandposition.vert deleted file mode 100644 index 34849eae..00000000 --- a/src/datavisualization/engine/shaders/colorandposition.vert +++ /dev/null @@ -1,10 +0,0 @@ -uniform highp mat4 MVP; - -attribute highp vec3 vertexPosition_mdl; - -varying highp vec3 pos; - -void main() { - gl_Position = MVP * vec4(vertexPosition_mdl, 1.0); - pos = vertexPosition_mdl; -} diff --git a/src/datavisualization/engine/shaders/position.vert b/src/datavisualization/engine/shaders/position.vert new file mode 100644 index 00000000..34849eae --- /dev/null +++ b/src/datavisualization/engine/shaders/position.vert @@ -0,0 +1,10 @@ +uniform highp mat4 MVP; + +attribute highp vec3 vertexPosition_mdl; + +varying highp vec3 pos; + +void main() { + gl_Position = MVP * vec4(vertexPosition_mdl, 1.0); + pos = vertexPosition_mdl; +} diff --git a/src/datavisualization/engine/shaders/positionmap.frag b/src/datavisualization/engine/shaders/positionmap.frag new file mode 100644 index 00000000..9a277ab8 --- /dev/null +++ b/src/datavisualization/engine/shaders/positionmap.frag @@ -0,0 +1,11 @@ +varying highp vec3 pos; + +void main() { + // This shader encodes the axis position into the vertex color, assuming the object + // is a cube filling the entire data area of the graph + gl_FragColor = vec4((pos.x + 1.0) / 2.0, + (pos.y + 1.0) / 2.0, + (-pos.z + 1.0) / 2.0, + 0.0); +} + diff --git a/src/datavisualization/engine/surface3drenderer.cpp b/src/datavisualization/engine/surface3drenderer.cpp index 9121e37c..3d39a71d 100644 --- a/src/datavisualization/engine/surface3drenderer.cpp +++ b/src/datavisualization/engine/surface3drenderer.cpp @@ -131,9 +131,6 @@ void Surface3DRenderer::initializeOpenGL() loadGridLineMesh(); #endif - // Load label mesh - loadLabelMesh(); - // Resize in case we've missed resize events // Resize calls initSelectionBuffer and initDepthBuffer, so they don't need to be called here handleResize(); @@ -1297,6 +1294,13 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) } #endif + // Do position mapping when necessary + if (m_graphPositionQueryPending) { + QVector3D graphDimensions(m_scaleX, m_scaleY, m_scaleZ); + queriedGraphPosition(projectionViewMatrix, graphDimensions, defaultFboHandle); + emit needRender(); + } + // Draw selection buffer if (!m_cachedIsSlicingActivated && (!m_renderCacheList.isEmpty() || !m_customRenderCache.isEmpty()) @@ -2830,8 +2834,11 @@ void Surface3DRenderer::updateSlicingActive(bool isSlicing) m_cachedIsSlicingActivated = isSlicing; - if (!m_cachedIsSlicingActivated) - initSelectionBuffer(); // We need to re-init selection buffer in case there has been a resize + if (!m_cachedIsSlicingActivated) { + // We need to re-init selection buffer in case there has been a resize + initSelectionBuffer(); + initCursorPositionBuffer(); + } #if !defined(QT_OPENGL_ES_2) updateDepthBuffer(); // Re-init depth buffer as well diff --git a/src/datavisualization/input/q3dinputhandler.cpp b/src/datavisualization/input/q3dinputhandler.cpp index e6f3de24..0bed3cb9 100644 --- a/src/datavisualization/input/q3dinputhandler.cpp +++ b/src/datavisualization/input/q3dinputhandler.cpp @@ -18,13 +18,16 @@ #include "datavisualizationglobal_p.h" #include "q3dinputhandler_p.h" +#include "abstract3dcontroller_p.h" QT_BEGIN_NAMESPACE_DATAVISUALIZATION -const int minZoomLevel = 10; -const int halfSizeZoomLevel = 50; -const int oneToOneZoomLevel = 100; -const int maxZoomLevel = 500; +const int minZoomLevel = 10; +const int halfSizeZoomLevel = 50; +const int oneToOneZoomLevel = 100; +const int maxZoomLevel = 500; +const int driftTowardCenterLevel = 175; +const float wheelZoomDrift = 0.1f; const int nearZoomRangeDivider = 12; const int midZoomRangeDivider = 60; @@ -84,6 +87,7 @@ const float rotationSpeed = 100.0f; * \since QtDataVisualization 1.2 * * This property specifies if this input handler allows graph rotation. + * Defaults to \c{true}. */ /*! @@ -91,6 +95,7 @@ const float rotationSpeed = 100.0f; * \since QtDataVisualization 1.2 * * This property specifies if this input handler allows graph zooming. + * Defaults to \c{true}. */ /*! @@ -98,6 +103,16 @@ const float rotationSpeed = 100.0f; * \since QtDataVisualization 1.2 * * This property specifies if this input handler allows selection from the graph. + * Defaults to \c{true}. + */ + +/*! + * \qmlproperty bool InputHandler3D::zoomAtTargetEnabled + * \since QtDataVisualization 1.2 + * + * This property specifies if zooming changes the camera target to the position of the input + * at the time of the zoom. + * Defaults to \c{true}. */ /*! @@ -236,7 +251,16 @@ void Q3DInputHandler::wheelEvent(QWheelEvent *event) else if (zoomLevel < minZoomLevel) zoomLevel = minZoomLevel; - scene()->activeCamera()->setZoomLevel(zoomLevel); + if (isZoomAtTargetEnabled()) { + scene()->setGraphPositionQuery(event->pos()); + d_ptr->m_zoomAtTargetPending = true; + // If zoom at target is enabled, we don't want to zoom yet, as that causes + // jitter. Instead, we zoom next frame, when we apply the camera position. + d_ptr->m_requestedZoomLevel = zoomLevel; + d_ptr->m_driftMultiplier = wheelZoomDrift; + } else { + scene()->activeCamera()->setZoomLevel(zoomLevel); + } } } @@ -245,6 +269,7 @@ void Q3DInputHandler::wheelEvent(QWheelEvent *event) * \since QtDataVisualization 1.2 * * This property specifies if this input handler allows graph rotation. + * Defaults to \c{true}. */ void Q3DInputHandler::setRotationEnabled(bool enable) { @@ -264,6 +289,7 @@ bool Q3DInputHandler::isRotationEnabled() const * \since QtDataVisualization 1.2 * * This property specifies if this input handler allows graph zooming. + * Defaults to \c{true}. */ void Q3DInputHandler::setZoomEnabled(bool enable) { @@ -283,6 +309,7 @@ bool Q3DInputHandler::isZoomEnabled() const * \since QtDataVisualization 1.2 * * This property specifies if this input handler allows selection from the graph. + * Defaults to \c{true}. */ void Q3DInputHandler::setSelectionEnabled(bool enable) { @@ -297,17 +324,100 @@ bool Q3DInputHandler::isSelectionEnabled() const return d_ptr->m_selectionEnabled; } +/*! + * \property Q3DInputHandler::zoomAtTargetEnabled + * \since QtDataVisualization 1.2 + * + * This property specifies if zooming should change the camera target so that the zoomed point + * of the graph stays at the same location after the zoom. + * Defaults to \c{true}. + */ +void Q3DInputHandler::setZoomAtTargetEnabled(bool enable) +{ + if (d_ptr->m_zoomAtTargetEnabled != enable) { + d_ptr->m_zoomAtTargetEnabled = enable; + emit zoomAtTargetEnabledChanged(enable); + } +} + +bool Q3DInputHandler::isZoomAtTargetEnabled() const +{ + return d_ptr->m_zoomAtTargetEnabled; +} + Q3DInputHandlerPrivate::Q3DInputHandlerPrivate(Q3DInputHandler *q) : q_ptr(q), m_inputState(QAbstract3DInputHandlerPrivate::InputStateNone), m_rotationEnabled(true), m_zoomEnabled(true), - m_selectionEnabled(true) + m_selectionEnabled(true), + m_zoomAtTargetEnabled(true), + m_zoomAtTargetPending(false), + m_controller(0), + m_requestedZoomLevel(0.0f), + m_driftMultiplier(0.0f) { + QObject::connect(q, &QAbstract3DInputHandler::sceneChanged, + this, &Q3DInputHandlerPrivate::handleSceneChange); } Q3DInputHandlerPrivate::~Q3DInputHandlerPrivate() { } +void Q3DInputHandlerPrivate::handleSceneChange(Q3DScene *scene) +{ + if (scene) { + if (m_controller) { + QObject::disconnect(m_controller, &Abstract3DController::queriedGraphPositionChanged, + this, &Q3DInputHandlerPrivate::handleQueriedGraphPositionChange); + } + + m_controller = qobject_cast(scene->parent()); + + if (m_controller) { + QObject::connect(m_controller, &Abstract3DController::queriedGraphPositionChanged, + this, &Q3DInputHandlerPrivate::handleQueriedGraphPositionChange); + } + } +} + +void Q3DInputHandlerPrivate::handleQueriedGraphPositionChange() +{ + if (m_zoomAtTargetPending) { + // Check if the zoom point is on graph + QVector3D newTarget = m_controller->queriedGraphPosition(); + float currentZoom = m_requestedZoomLevel; + float previousZoom = q_ptr->scene()->activeCamera()->zoomLevel(); + q_ptr->scene()->activeCamera()->setZoomLevel(currentZoom); + float diffAdj = 0.0f; + + // If zooming in/out outside the graph, or zooming out after certain point, + // move towards the center. + if ((qAbs(newTarget.x()) > 1.0f + || qAbs(newTarget.y()) > 1.0f + || qAbs(newTarget.z()) > 1.0f) + || (previousZoom > currentZoom && currentZoom <= driftTowardCenterLevel)) { + newTarget = zeroVector; + // Add some extra correction so that we actually reach the center eventually + diffAdj = m_driftMultiplier; + if (previousZoom > currentZoom) + diffAdj *= 2.0f; // Correct towards center little more when zooming out + } + + float zoomFraction = 1.0f - (previousZoom / currentZoom); + + // Adjust camera towards the zoom point, attempting to keep the cursor at same graph point + QVector3D oldTarget = q_ptr->scene()->activeCamera()->target(); + QVector3D origDiff = newTarget - oldTarget; + QVector3D diff = origDiff * zoomFraction + (origDiff.normalized() * diffAdj); + if (diff.length() > origDiff.length()) + diff = origDiff; + q_ptr->scene()->activeCamera()->setTarget(oldTarget + diff); + + if (q_ptr->scene()->selectionQueryPosition() == Q3DScene::invalidSelectionPoint()) + m_zoomAtTargetPending = false; + } +} + QT_END_NAMESPACE_DATAVISUALIZATION diff --git a/src/datavisualization/input/q3dinputhandler.h b/src/datavisualization/input/q3dinputhandler.h index 9afeb945..5423b4ea 100644 --- a/src/datavisualization/input/q3dinputhandler.h +++ b/src/datavisualization/input/q3dinputhandler.h @@ -31,6 +31,7 @@ class QT_DATAVISUALIZATION_EXPORT Q3DInputHandler : public QAbstract3DInputHandl Q_PROPERTY(bool rotationEnabled READ isRotationEnabled WRITE setRotationEnabled NOTIFY rotationEnabledChanged) Q_PROPERTY(bool zoomEnabled READ isZoomEnabled WRITE setZoomEnabled NOTIFY zoomEnabledChanged) Q_PROPERTY(bool selectionEnabled READ isSelectionEnabled WRITE setSelectionEnabled NOTIFY selectionEnabledChanged) + Q_PROPERTY(bool zoomAtTargetEnabled READ isZoomAtTargetEnabled WRITE setZoomAtTargetEnabled NOTIFY zoomAtTargetEnabledChanged) public: explicit Q3DInputHandler(QObject *parent = 0); @@ -42,6 +43,8 @@ public: bool isZoomEnabled() const; void setSelectionEnabled(bool enable); bool isSelectionEnabled() const; + void setZoomAtTargetEnabled(bool enable); + bool isZoomAtTargetEnabled() const; // Input event listeners virtual void mousePressEvent(QMouseEvent *event, const QPoint &mousePos); @@ -53,6 +56,7 @@ signals: void rotationEnabledChanged(bool enable); void zoomEnabledChanged(bool enable); void selectionEnabledChanged(bool enable); + void zoomAtTargetEnabledChanged(bool enable); private: Q_DISABLE_COPY(Q3DInputHandler) diff --git a/src/datavisualization/input/q3dinputhandler_p.h b/src/datavisualization/input/q3dinputhandler_p.h index a3ed8c9c..79b1c8dd 100644 --- a/src/datavisualization/input/q3dinputhandler_p.h +++ b/src/datavisualization/input/q3dinputhandler_p.h @@ -34,19 +34,34 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION -class Q3DInputHandlerPrivate +class Abstract3DController; + +class Q3DInputHandlerPrivate : public QObject { + Q_OBJECT public: Q3DInputHandlerPrivate(Q3DInputHandler *q); ~Q3DInputHandlerPrivate(); -protected: +public slots: + void handleSceneChange(Q3DScene *scene); + void handleQueriedGraphPositionChange(); + +private: Q3DInputHandler *q_ptr; +protected: QAbstract3DInputHandlerPrivate::InputState m_inputState; bool m_rotationEnabled; bool m_zoomEnabled; bool m_selectionEnabled; + bool m_zoomAtTargetEnabled; + bool m_zoomAtTargetPending; + + Abstract3DController *m_controller; // Not owned + + float m_requestedZoomLevel; + float m_driftMultiplier; friend class Q3DInputHandler; }; diff --git a/src/datavisualization/input/qtouch3dinputhandler.cpp b/src/datavisualization/input/qtouch3dinputhandler.cpp index 5d62922b..b811548b 100644 --- a/src/datavisualization/input/qtouch3dinputhandler.cpp +++ b/src/datavisualization/input/qtouch3dinputhandler.cpp @@ -33,6 +33,7 @@ const int tapAndHoldTime = 250; const float rotationSpeed = 200.0f; const int minZoomLevel = 10; const int maxZoomLevel = 500; +const float touchZoomDrift = 0.02f; /*! * \class QTouch3DInputHandler @@ -113,7 +114,8 @@ void QTouch3DInputHandler::touchEvent(QTouchEvent *event) if (!scene()->isSlicingActive() && points.count() == 2) { d_ptr->m_holdTimer->stop(); QPointF distance = points.at(0).pos() - points.at(1).pos(); - d_ptr->handlePinchZoom(distance.manhattanLength()); + QPoint midPoint = ((points.at(0).pos() + points.at(1).pos()) / 2.0).toPoint(); + d_ptr->handlePinchZoom(distance.manhattanLength(), midPoint); } else if (points.count() == 1) { QPointF pointerPos = points.at(0).pos(); if (event->type() == QEvent::TouchBegin) { @@ -165,7 +167,8 @@ void QTouch3DInputHandler::touchEvent(QTouchEvent *event) } QTouch3DInputHandlerPrivate::QTouch3DInputHandlerPrivate(QTouch3DInputHandler *q) - : q_ptr(q), + : Q3DInputHandlerPrivate(q), + q_ptr(q), m_holdTimer(0), m_inputState(QAbstract3DInputHandlerPrivate::InputStateNone) { @@ -181,7 +184,7 @@ QTouch3DInputHandlerPrivate::~QTouch3DInputHandlerPrivate() delete m_holdTimer; } -void QTouch3DInputHandlerPrivate::handlePinchZoom(float distance) +void QTouch3DInputHandlerPrivate::handlePinchZoom(float distance, const QPoint &pos) { if (q_ptr->isZoomEnabled()) { int newDistance = distance; @@ -200,7 +203,18 @@ void QTouch3DInputHandlerPrivate::handlePinchZoom(float distance) zoomLevel = maxZoomLevel; else if (zoomLevel < minZoomLevel) zoomLevel = minZoomLevel; - camera->setZoomLevel(zoomLevel); + + if (q_ptr->isZoomAtTargetEnabled()) { + q_ptr->scene()->setGraphPositionQuery(pos); + m_zoomAtTargetPending = true; + // If zoom at target is enabled, we don't want to zoom yet, as that causes + // jitter. Instead, we zoom next frame, when we apply the camera position. + m_requestedZoomLevel = zoomLevel; + m_driftMultiplier = touchZoomDrift; + } else { + camera->setZoomLevel(zoomLevel); + } + q_ptr->setPrevDistance(newDistance); } } diff --git a/src/datavisualization/input/qtouch3dinputhandler_p.h b/src/datavisualization/input/qtouch3dinputhandler_p.h index 613b5f28..b01904ca 100644 --- a/src/datavisualization/input/qtouch3dinputhandler_p.h +++ b/src/datavisualization/input/qtouch3dinputhandler_p.h @@ -19,7 +19,7 @@ #ifndef QTOUCH3DINPUTHANDLER_P_H #define QTOUCH3DINPUTHANDLER_P_H -#include "qabstract3dinputhandler_p.h" +#include "q3dinputhandler_p.h" #include "qtouch3dinputhandler.h" class QTimer; @@ -28,7 +28,7 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION class QAbstract3DInputHandler; -class QTouch3DInputHandlerPrivate : public QObject +class QTouch3DInputHandlerPrivate : public Q3DInputHandlerPrivate { Q_OBJECT @@ -36,13 +36,14 @@ public: QTouch3DInputHandlerPrivate(QTouch3DInputHandler *q); ~QTouch3DInputHandlerPrivate(); - void handlePinchZoom(float distance); + void handlePinchZoom(float distance, const QPoint &pos); void handleTapAndHold(); void handleSelection(const QPointF &position); void handleRotation(const QPointF &position); -public: +private: QTouch3DInputHandler *q_ptr; +public: QTimer *m_holdTimer; QAbstract3DInputHandlerPrivate::InputState m_inputState; QPointF m_startHoldPos; diff --git a/src/datavisualization/utils/texturehelper.cpp b/src/datavisualization/utils/texturehelper.cpp index 5e21b5cd..9dbf0613 100644 --- a/src/datavisualization/utils/texturehelper.cpp +++ b/src/datavisualization/utils/texturehelper.cpp @@ -218,6 +218,31 @@ GLuint TextureHelper::createSelectionTexture(const QSize &size, GLuint &frameBuf return textureid; } +GLuint TextureHelper::createCursorPositionTexture(const QSize &size, GLuint &frameBuffer) +{ + GLuint textureid; + glGenTextures(1, &textureid); + glBindTexture(GL_TEXTURE_2D, textureid); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size.width(), size.height(), 0, GL_RGBA, + GL_UNSIGNED_BYTE, NULL); + glBindTexture(GL_TEXTURE_2D, 0); + + glGenFramebuffers(1, &frameBuffer); + glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, + textureid, 0); + + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + if (status != GL_FRAMEBUFFER_COMPLETE) { + qCritical() << "Cursor position mapper frame buffer creation failed:" << status; + glDeleteTextures(1, &textureid); + textureid = 0; + } + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + return textureid; +} + GLuint TextureHelper::createUniformTexture(const QColor &color) { QImage image(QSize(int(uniformTextureWidth), int(uniformTextureHeight)), diff --git a/src/datavisualization/utils/texturehelper_p.h b/src/datavisualization/utils/texturehelper_p.h index c1dfe8c9..03080b2a 100644 --- a/src/datavisualization/utils/texturehelper_p.h +++ b/src/datavisualization/utils/texturehelper_p.h @@ -55,6 +55,7 @@ class TextureHelper : protected QOpenGLFunctions GLuint createCubeMapTexture(const QImage &image, bool useTrilinearFiltering = false); // Returns selection texture and inserts generated framebuffers to framebuffer parameters GLuint createSelectionTexture(const QSize &size, GLuint &frameBuffer, GLuint &depthBuffer); + GLuint createCursorPositionTexture(const QSize &size, GLuint &frameBuffer); GLuint createUniformTexture(const QColor &color); GLuint createGradientTexture(const QLinearGradient &gradient); #if !defined(QT_OPENGL_ES_2) @@ -75,6 +76,7 @@ class TextureHelper : protected QOpenGLFunctions friend class Bars3DRenderer; friend class Surface3DRenderer; friend class Scatter3DRenderer; + friend class Abstract3DRenderer; }; QT_END_NAMESPACE_DATAVISUALIZATION diff --git a/src/datavisualizationqml2/abstractdeclarative.cpp b/src/datavisualizationqml2/abstractdeclarative.cpp index fdfa55d8..9206973a 100644 --- a/src/datavisualizationqml2/abstractdeclarative.cpp +++ b/src/datavisualizationqml2/abstractdeclarative.cpp @@ -341,6 +341,8 @@ void AbstractDeclarative::setSharedController(Abstract3DController *controller) &AbstractDeclarative::reflectivityChanged); QObject::connect(m_controller.data(), &Abstract3DController::localeChanged, this, &AbstractDeclarative::localeChanged); + QObject::connect(m_controller.data(), &Abstract3DController::queriedGraphPositionChanged, this, + &AbstractDeclarative::queriedGraphPositionChanged); } void AbstractDeclarative::activateOpenGLContext(QQuickWindow *window) @@ -796,6 +798,11 @@ QLocale AbstractDeclarative::locale() const return m_controller->locale(); } +QVector3D AbstractDeclarative::queriedGraphPosition() const +{ + return m_controller->queriedGraphPosition(); +} + void AbstractDeclarative::windowDestroyed(QObject *obj) { // Remove destroyed window from window lists diff --git a/src/datavisualizationqml2/abstractdeclarative_p.h b/src/datavisualizationqml2/abstractdeclarative_p.h index 52a5956b..8dc260c2 100644 --- a/src/datavisualizationqml2/abstractdeclarative_p.h +++ b/src/datavisualizationqml2/abstractdeclarative_p.h @@ -78,6 +78,7 @@ class AbstractDeclarative : public QQuickItem Q_PROPERTY(bool reflection READ isReflection WRITE setReflection NOTIFY reflectionChanged REVISION 2) Q_PROPERTY(qreal reflectivity READ reflectivity WRITE setReflectivity NOTIFY reflectivityChanged REVISION 2) Q_PROPERTY(QLocale locale READ locale WRITE setLocale NOTIFY localeChanged REVISION 2) + Q_PROPERTY(QVector3D queriedGraphPosition READ queriedGraphPosition NOTIFY queriedGraphPositionChanged REVISION 2) public: enum SelectionFlag { @@ -217,6 +218,8 @@ public: void setLocale(const QLocale &locale); QLocale locale() const; + QVector3D queriedGraphPosition() const; + public slots: virtual void handleAxisXChanged(QAbstract3DAxis *axis) = 0; virtual void handleAxisYChanged(QAbstract3DAxis *axis) = 0; @@ -260,6 +263,7 @@ signals: Q_REVISION(2) void reflectionChanged(bool enabled); Q_REVISION(2) void reflectivityChanged(qreal reflectivity); Q_REVISION(2) void localeChanged(const QLocale &locale); + Q_REVISION(2) void queriedGraphPositionChanged(const QVector3D &data); private: QPointer m_controller; diff --git a/src/datavisualizationqml2/datavisualizationqml2_plugin.cpp b/src/datavisualizationqml2/datavisualizationqml2_plugin.cpp index a22fc55c..5aaebf03 100644 --- a/src/datavisualizationqml2/datavisualizationqml2_plugin.cpp +++ b/src/datavisualizationqml2/datavisualizationqml2_plugin.cpp @@ -113,6 +113,8 @@ void QtDataVisualizationQml2Plugin::registerTypes(const char *uri) // New revisions qmlRegisterUncreatableType(uri, 1, 2, "AbstractGraph3D", QLatin1String("Trying to create uncreatable: AbstractGraph3D.")); + qmlRegisterUncreatableType(uri, 1, 2, "Scene3D", + QLatin1String("Trying to create uncreatable: Scene3D.")); qmlRegisterType(uri, 1, 2, "Surface3D"); qmlRegisterType(uri, 1, 2, "Camera3D"); qmlRegisterType(uri, 1, 2, "Custom3DItem"); diff --git a/tests/barstest/chart.cpp b/tests/barstest/chart.cpp index 9a81fc86..d7b23719 100644 --- a/tests/barstest/chart.cpp +++ b/tests/barstest/chart.cpp @@ -203,6 +203,7 @@ GraphModifier::GraphModifier(Q3DBars *barchart, QColorDialog *colorDialog) m_graph->activeTheme()->setFont(QFont("Times Roman", 20)); + // Release and store the default input handler. m_defaultInputHandler = static_cast(m_graph->activeInputHandler()); m_graph->releaseInputHandler(m_defaultInputHandler); @@ -1421,6 +1422,11 @@ void GraphModifier::setInputHandlerSelectionEnabled(int enabled) m_defaultInputHandler->setSelectionEnabled(enabled); } +void GraphModifier::setInputHandlerZoomAtTargetEnabled(int enabled) +{ + m_defaultInputHandler->setZoomAtTargetEnabled(enabled); +} + void GraphModifier::changeValueAxisSegments(int value) { qDebug() << __FUNCTION__ << value; diff --git a/tests/barstest/chart.h b/tests/barstest/chart.h index eac6d3f1..93abbfa4 100644 --- a/tests/barstest/chart.h +++ b/tests/barstest/chart.h @@ -97,6 +97,7 @@ public: void setInputHandlerRotationEnabled(int enabled); void setInputHandlerZoomEnabled(int enabled); void setInputHandlerSelectionEnabled(int enabled); + void setInputHandlerZoomAtTargetEnabled(int enabled); void setReflection(bool enabled); void setReflectivity(int value); void toggleCustomItem(); diff --git a/tests/barstest/main.cpp b/tests/barstest/main.cpp index 1cc47859..cffbf4b4 100644 --- a/tests/barstest/main.cpp +++ b/tests/barstest/main.cpp @@ -234,6 +234,10 @@ int main(int argc, char **argv) inputHandlerSelectionCheckBox->setText("IH: Allow selection"); inputHandlerSelectionCheckBox->setChecked(true); + QCheckBox *inputHandlerZoomAtTargetCheckBox = new QCheckBox(widget); + inputHandlerZoomAtTargetCheckBox->setText("IH: setZoomAtTarget"); + inputHandlerZoomAtTargetCheckBox->setChecked(false); + QSlider *rotationSliderX = new QSlider(Qt::Horizontal, widget); rotationSliderX->setTickInterval(1); rotationSliderX->setMinimum(-180); @@ -422,6 +426,7 @@ int main(int argc, char **argv) vLayout3->addWidget(inputHandlerRotationCheckBox, 0, Qt::AlignTop); vLayout3->addWidget(inputHandlerZoomCheckBox, 0, Qt::AlignTop); vLayout3->addWidget(inputHandlerSelectionCheckBox, 0, Qt::AlignTop); + vLayout3->addWidget(inputHandlerZoomAtTargetCheckBox, 0, Qt::AlignTop); vLayout3->addWidget(new QLabel(QStringLiteral("Adjust shadow quality")), 0, Qt::AlignTop); vLayout3->addWidget(shadowQuality, 0, Qt::AlignTop); vLayout3->addWidget(new QLabel(QStringLiteral("Change font")), 0, Qt::AlignTop); @@ -559,6 +564,8 @@ int main(int argc, char **argv) &GraphModifier::setInputHandlerZoomEnabled); QObject::connect(inputHandlerSelectionCheckBox, &QCheckBox::stateChanged, modifier, &GraphModifier::setInputHandlerSelectionEnabled); + QObject::connect(inputHandlerZoomAtTargetCheckBox, &QCheckBox::stateChanged, modifier, + &GraphModifier::setInputHandlerZoomAtTargetEnabled); QObject::connect(rotationCheckBox, &QCheckBox::stateChanged, modifier, &GraphModifier::setUseNullInputHandler); diff --git a/tests/scattertest/scatterchart.cpp b/tests/scattertest/scatterchart.cpp index 35d56236..a45c090b 100644 --- a/tests/scattertest/scatterchart.cpp +++ b/tests/scattertest/scatterchart.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include using namespace QtDataVisualization; @@ -46,6 +47,7 @@ ScatterDataModifier::ScatterDataModifier(Q3DScatter *scatter) m_chart->setAxisX(new QValue3DAxis); m_chart->setAxisY(new QValue3DAxis); m_chart->setAxisZ(new QValue3DAxis); + static_cast(m_chart->activeInputHandler())->setZoomAtTargetEnabled(true); createAndAddSeries(); createAndAddSeries(); @@ -435,6 +437,10 @@ void ScatterDataModifier::testAxisReverse() m_chart->axisX()->setRange(0.0f, 10.0f); m_chart->axisY()->setRange(-20.0f, 50.0f); m_chart->axisZ()->setRange(5.0f, 15.0f); + m_chart->axisX()->setTitle("Axis X"); + m_chart->axisZ()->setTitle("Axis Z"); + m_chart->axisX()->setTitleVisible(true); + m_chart->axisZ()->setTitleVisible(true); m_chart->addSeries(series0); m_chart->addSeries(series1); } diff --git a/tests/surfacetest/graphmodifier.cpp b/tests/surfacetest/graphmodifier.cpp index a22ede2b..1a069cf4 100644 --- a/tests/surfacetest/graphmodifier.cpp +++ b/tests/surfacetest/graphmodifier.cpp @@ -21,18 +21,21 @@ #include #include #include +#include #include #include #include #include - +#ifndef QT_NO_CURSOR +#include +#endif using namespace QtDataVisualization; //#define JITTER_PLANE //#define WONKY_PLANE -GraphModifier::GraphModifier(Q3DSurface *graph) +GraphModifier::GraphModifier(Q3DSurface *graph, QWidget *parentWidget) : m_graph(graph), m_series1(new QSurface3DSeries), m_series2(new QSurface3DSeries), @@ -63,7 +66,8 @@ GraphModifier::GraphModifier(Q3DSurface *graph) m_drawMode2(QSurface3DSeries::DrawSurfaceAndWireframe), m_drawMode3(QSurface3DSeries::DrawSurfaceAndWireframe), m_drawMode4(QSurface3DSeries::DrawSurfaceAndWireframe), - m_offset(4.0f) + m_offset(4.0f), + m_parentWidget(parentWidget) { m_graph->setAxisX(new QValue3DAxis); m_graph->axisX()->setTitle("X-Axis"); @@ -97,6 +101,8 @@ GraphModifier::GraphModifier(Q3DSurface *graph) m_graph->axisY()->setRange(m_minY, m_minY + m_rangeY); m_graph->axisZ()->setRange(m_minZ, m_minZ + m_rangeZ); + static_cast(m_graph->activeInputHandler())->setZoomAtTargetEnabled(true); + for (int i = 0; i < 4; i++) { m_multiseries[i] = new QSurface3DSeries; m_multiseries[i]->setName(QStringLiteral("Series %1").arg(i+1)); @@ -109,6 +115,7 @@ GraphModifier::GraphModifier(Q3DSurface *graph) m_theSeries->setItemLabelFormat(QStringLiteral("@seriesName: (@xLabel, @zLabel): @yLabel")); connect(&m_timer, &QTimer::timeout, this, &GraphModifier::timeout); + connect(&m_graphPositionQueryTimer, &QTimer::timeout, this, &GraphModifier::graphQueryTimeout); connect(m_theSeries, &QSurface3DSeries::selectedPointChanged, this, &GraphModifier::selectedPointChanged); QObject::connect(m_graph, &Q3DSurface::axisXChanged, this, @@ -119,6 +126,8 @@ GraphModifier::GraphModifier(Q3DSurface *graph) &GraphModifier::handleAxisZChanged); QObject::connect(m_graph, &QAbstract3DGraph::currentFpsChanged, this, &GraphModifier::handleFpsChange); + + //m_graphPositionQueryTimer.start(100); } GraphModifier::~GraphModifier() @@ -691,6 +700,16 @@ void GraphModifier::timeout() m_theSeries->dataProxy()->resetArray(m_planeArray); } +void GraphModifier::graphQueryTimeout() +{ +#ifndef QT_NO_CURSOR + m_graph->scene()->setGraphPositionQuery(m_parentWidget->mapFromGlobal(QCursor::pos())); + qDebug() << "pos: " << (m_parentWidget->mapFromGlobal(QCursor::pos())); +#else + m_graph->scene()->setGraphPositionQuery(QPoint(100, 100)); +#endif +} + void GraphModifier::handleAxisXChanged(QValue3DAxis *axis) { qDebug() << __FUNCTION__ << axis << axis->orientation() << (axis == m_graph->axisX()); diff --git a/tests/surfacetest/graphmodifier.h b/tests/surfacetest/graphmodifier.h index f461f022..d1c77940 100644 --- a/tests/surfacetest/graphmodifier.h +++ b/tests/surfacetest/graphmodifier.h @@ -41,7 +41,7 @@ public: Map }; - explicit GraphModifier(Q3DSurface *graph); + explicit GraphModifier(Q3DSurface *graph, QWidget *parentWidget); ~GraphModifier(); void toggleSeries1(bool enabled); @@ -122,6 +122,7 @@ public slots: void flipViews(); void changeSelectionMode(int mode); void timeout(); + void graphQueryTimeout(); void handleAxisXChanged(QValue3DAxis *axis); void handleAxisYChanged(QValue3DAxis *axis); @@ -190,6 +191,8 @@ private: float m_multiSampleOffsetZ[4]; QSurfaceDataArray m_massiveTestCacheArray; QVector3D m_cameraTarget; + QWidget *m_parentWidget; + QTimer m_graphPositionQueryTimer; }; #endif diff --git a/tests/surfacetest/main.cpp b/tests/surfacetest/main.cpp index f89e6c50..9e14c665 100644 --- a/tests/surfacetest/main.cpp +++ b/tests/surfacetest/main.cpp @@ -524,7 +524,7 @@ int main(int argc, char *argv[]) widget->show(); - GraphModifier *modifier = new GraphModifier(surfaceGraph); + GraphModifier *modifier = new GraphModifier(surfaceGraph, container); // Connect controls to slots on modifier QObject::connect(smoothCB, &QCheckBox::stateChanged, diff --git a/tests/volumetrictest/volumetrictest.pro b/tests/volumetrictest/volumetrictest.pro index 137e5bab..df3ef0dd 100644 --- a/tests/volumetrictest/volumetrictest.pro +++ b/tests/volumetrictest/volumetrictest.pro @@ -1,7 +1,3 @@ -android|ios { - error( "This example is not supported for android or ios." ) -} - !include( ../tests.pri ) { error( "Couldn't find the tests.pri file!" ) } -- cgit v1.2.3 From 23717be4663a087d4d906da3e7c2c751dc2e07d5 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Fri, 26 Sep 2014 12:11:50 +0300 Subject: Fix absolutely scaled volumes Min/max bounds were set incorrectly. Change-Id: I54e115ef98321531410bcb64146ae32230375d4e Reviewed-by: Mika Salmela --- src/datavisualization/engine/abstract3drenderer.cpp | 5 +++-- tests/qmlvolume/qml/qmlvolume/main.qml | 11 ++++++++--- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/datavisualization/engine/abstract3drenderer.cpp b/src/datavisualization/engine/abstract3drenderer.cpp index a5e1bc9b..ab086b5f 100644 --- a/src/datavisualization/engine/abstract3drenderer.cpp +++ b/src/datavisualization/engine/abstract3drenderer.cpp @@ -1171,8 +1171,9 @@ void Abstract3DRenderer::recalculateCustomItemScalingAndPos(CustomRenderItem *it item->setScaling(item->origScaling()); item->setPosition(item->origPosition()); if (item->isVolume()) { - item->setMinBounds(-1.0f * zeroVector); - item->setMaxBounds(oneVector); + // Y and Z need to be flipped as shader flips those axes + item->setMinBounds(QVector3D(-1.0f, 1.0f, 1.0f)); + item->setMaxBounds(QVector3D(1.0f, -1.0f, -1.0f)); } } QVector3D translation = convertPositionToTranslation(item->position(), diff --git a/tests/qmlvolume/qml/qmlvolume/main.qml b/tests/qmlvolume/qml/qmlvolume/main.qml index 7af94b21..aec5f075 100644 --- a/tests/qmlvolume/qml/qmlvolume/main.qml +++ b/tests/qmlvolume/qml/qmlvolume/main.qml @@ -39,7 +39,7 @@ Item { width: dataView.width height: dataView.height orthoProjection: true - measureFps: true + //measureFps: true onCurrentFpsChanged: { if (fps > 10) @@ -125,10 +125,15 @@ Item { text: "Slice" onClicked: { - if (volumeItem.sliceIndexZ == -1) + if (volumeItem.sliceIndexZ == -1) { volumeItem.sliceIndexZ = 128 - else + volumeItem.drawSlices = true + volumeItem.drawSliceFrames = true + } else { volumeItem.sliceIndexZ = -1 + volumeItem.drawSlices = false + volumeItem.drawSliceFrames = false + } } } NewButton { -- cgit v1.2.3 From 5b8b37ebe42f9fb3cdc0bb269146c18ebbd56ed3 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Fri, 26 Sep 2014 13:41:31 +0300 Subject: Allow setting bounds for camera zoom level MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Task-number: QTRD-3337 Change-Id: I503dc4402907a2fdfa74ca86698b0e98a23b3b08 Reviewed-by: Tomi Korpipää --- .../datavisualization/volumetric/volumetric.cpp | 12 +-- examples/datavisualization/volumetric/volumetric.h | 1 - src/datavisualization/engine/q3dcamera.cpp | 118 +++++++++++++++++++-- src/datavisualization/engine/q3dcamera.h | 8 ++ src/datavisualization/engine/q3dcamera_p.h | 16 +-- src/datavisualization/input/q3dinputhandler.cpp | 32 +++--- .../input/qtouch3dinputhandler.cpp | 27 +++-- 7 files changed, 154 insertions(+), 60 deletions(-) diff --git a/examples/datavisualization/volumetric/volumetric.cpp b/examples/datavisualization/volumetric/volumetric.cpp index 65599c31..ade74fb2 100644 --- a/examples/datavisualization/volumetric/volumetric.cpp +++ b/examples/datavisualization/volumetric/volumetric.cpp @@ -87,8 +87,9 @@ VolumetricModifier::VolumetricModifier(Q3DScatter *scatter) //! [6] m_graph->activeTheme()->setBackgroundEnabled(false); - // Only allow zooming at the center to avoid clipping issues + // Only allow zooming at the center and limit the zoom to 200% to avoid clipping issues static_cast(m_graph->activeInputHandler())->setZoomAtTargetEnabled(false); + m_graph->scene()->activeCamera()->setMaxZoomLevel(200.0f); toggleAreaAll(true); @@ -204,8 +205,6 @@ VolumetricModifier::VolumetricModifier(Q3DScatter *scatter) m_graph->addCustomItem(warningLabel); #endif - QObject::connect(m_graph->scene()->activeCamera(), &Q3DCamera::zoomLevelChanged, this, - &VolumetricModifier::handleZoomLevelChange); QObject::connect(m_graph, &QAbstract3DGraph::currentFpsChanged, this, &VolumetricModifier::handleFpsChange); QObject::connect(&m_timer, &QTimer::timeout, this, @@ -310,13 +309,6 @@ void VolumetricModifier::adjustSliceZ(int value) } } -void VolumetricModifier::handleZoomLevelChange() -{ - // Zooming inside volumetric object causes ugly clipping issues, so restrict zoom level a bit - if (m_graph->scene()->activeCamera()->zoomLevel() > 200) - m_graph->scene()->activeCamera()->setZoomLevel(200); -} - void VolumetricModifier::handleFpsChange(qreal fps) { const QString fpsFormat = QStringLiteral("FPS: %1"); diff --git a/examples/datavisualization/volumetric/volumetric.h b/examples/datavisualization/volumetric/volumetric.h index 7a4e7179..8d28b524 100644 --- a/examples/datavisualization/volumetric/volumetric.h +++ b/examples/datavisualization/volumetric/volumetric.h @@ -50,7 +50,6 @@ public slots: void adjustSliceX(int value); void adjustSliceY(int value); void adjustSliceZ(int value); - void handleZoomLevelChange(); void handleFpsChange(qreal fps); void handleTimeout(); void toggleLowDetail(bool enabled); diff --git a/src/datavisualization/engine/q3dcamera.cpp b/src/datavisualization/engine/q3dcamera.cpp index 0c2a8d82..9c3e4c08 100644 --- a/src/datavisualization/engine/q3dcamera.cpp +++ b/src/datavisualization/engine/q3dcamera.cpp @@ -110,8 +110,36 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION /*! * \qmlproperty float Camera3D::zoomLevel * - * This property contains the the camera zoom level in percentage. 100.0 means there is no zoom - * in or out set in the camera. + * This property contains the the camera zoom level in percentage. The default value of \c{100.0} + * means there is no zoom in or out set in the camera. + * The value is limited within the bounds defined by minZoomLevel and maxZoomLevel properties. + * + * \sa minZoomLevel, maxZoomLevel + */ + +/*! + * \qmlproperty float Camera3D::minZoomLevel + * + * This property contains the the minimum allowed camera zoom level. + * If the new minimum level is more than the existing maximum level, the maximum level is + * adjusted to the new minimum as well. + * If current zoomLevel is outside the new bounds, it is adjusted as well. + * The minZoomLevel cannot be set below \c{1.0}. + * Defaults to \c{10.0}. + * + * \sa zoomLevel, maxZoomLevel + */ + +/*! + * \qmlproperty float Camera3D::maxZoomLevel + * + * This property contains the the maximum allowed camera zoom level. + * If the new maximum level is less than the existing minimum level, the minimum level is + * adjusted to the new maximum as well. + * If current zoomLevel is outside the new bounds, it is adjusted as well. + * Defaults to \c{500.0f}. + * + * \sa zoomLevel, minZoomLevel */ /*! @@ -191,6 +219,8 @@ void Q3DCamera::copyValuesFrom(const Q3DObject &source) d_ptr->m_wrapYRotation = sourceCamera.d_ptr->m_wrapYRotation; d_ptr->m_zoomLevel = sourceCamera.d_ptr->m_zoomLevel; + d_ptr->m_minZoomLevel = sourceCamera.d_ptr->m_minZoomLevel; + d_ptr->m_maxZoomLevel = sourceCamera.d_ptr->m_maxZoomLevel; d_ptr->m_activePreset = sourceCamera.d_ptr->m_activePreset; } @@ -401,8 +431,11 @@ void Q3DCamera::setCameraPreset(CameraPreset preset) /*! * \property Q3DCamera::zoomLevel * - * This property contains the the camera zoom level in percentage. \c 100.0f means there is no zoom - * in or out set in the camera. + * This property contains the the camera zoom level in percentage. The default value of \c{100.0f} + * means there is no zoom in or out set in the camera. + * The value is limited within the bounds defined by minZoomLevel and maxZoomLevel properties. + * + * \sa minZoomLevel, maxZoomLevel */ float Q3DCamera::zoomLevel() const { @@ -411,10 +444,73 @@ float Q3DCamera::zoomLevel() const void Q3DCamera::setZoomLevel(float zoomLevel) { - if (d_ptr->m_zoomLevel != zoomLevel) { - d_ptr->m_zoomLevel = zoomLevel; + float newZoomLevel = qBound(d_ptr->m_minZoomLevel, zoomLevel, d_ptr->m_maxZoomLevel); + + if (d_ptr->m_zoomLevel != newZoomLevel) { + d_ptr->m_zoomLevel = newZoomLevel; + setDirty(true); + emit zoomLevelChanged(newZoomLevel); + } +} + +/*! + * \property Q3DCamera::minZoomLevel + * + * This property contains the the minimum allowed camera zoom level. + * If the new minimum level is more than the existing maximum level, the maximum level is + * adjusted to the new minimum as well. + * If current zoomLevel is outside the new bounds, it is adjusted as well. + * The minZoomLevel cannot be set below \c{1.0f}. + * Defaults to \c{10.0f}. + * + * \sa zoomLevel, maxZoomLevel + */ +float Q3DCamera::minZoomLevel() const +{ + return d_ptr->m_minZoomLevel; +} + +void Q3DCamera::setMinZoomLevel(float zoomLevel) +{ + // Don't allow minimum to be below one, as that can cause zoom to break. + float newMinLevel = qMax(zoomLevel, 1.0f); + if (d_ptr->m_minZoomLevel != newMinLevel) { + d_ptr->m_minZoomLevel = newMinLevel; + if (d_ptr->m_maxZoomLevel < newMinLevel) + setMaxZoomLevel(newMinLevel); + setZoomLevel(d_ptr->m_zoomLevel); + setDirty(true); + emit minZoomLevelChanged(newMinLevel); + } +} + +/*! + * \property Q3DCamera::maxZoomLevel + * + * This property contains the the maximum allowed camera zoom level. + * If the new maximum level is less than the existing minimum level, the minimum level is + * adjusted to the new maximum as well. + * If current zoomLevel is outside the new bounds, it is adjusted as well. + * Defaults to \c{500.0f}. + * + * \sa zoomLevel, minZoomLevel + */ +float Q3DCamera::maxZoomLevel() const +{ + return d_ptr->m_maxZoomLevel; +} + +void Q3DCamera::setMaxZoomLevel(float zoomLevel) +{ + // Don't allow maximum to be below one, as that can cause zoom to break. + float newMaxLevel = qMax(zoomLevel, 1.0f); + if (d_ptr->m_maxZoomLevel != newMaxLevel) { + d_ptr->m_maxZoomLevel = newMaxLevel; + if (d_ptr->m_minZoomLevel > newMaxLevel) + setMinZoomLevel(newMaxLevel); + setZoomLevel(d_ptr->m_zoomLevel); setDirty(true); - emit zoomLevelChanged(zoomLevel); + emit maxZoomLevelChanged(newMaxLevel); } } @@ -461,12 +557,12 @@ void Q3DCamera::setWrapYRotation(bool isEnabled) /*! * Utility function that sets the camera rotations and distance.\a horizontal and \a vertical * define the camera rotations to be used. - * Optional \a zoom parameter can be given to set the zoom percentage of the camera in range of - * \c{10.0f - 500.0f}. + * Optional \a zoom parameter can be given to set the zoom percentage of the camera within + * the bounds defined by minZoomLevel and maxZoomLevel properties. */ void Q3DCamera::setCameraPosition(float horizontal, float vertical, float zoom) { - setZoomLevel(qBound(10.0f, zoom, 500.0f)); + setZoomLevel(zoom); setXRotation(horizontal); setYRotation(vertical); } @@ -524,6 +620,8 @@ Q3DCameraPrivate::Q3DCameraPrivate(Q3DCamera *q) : m_maxXRotation(180.0f), m_maxYRotation(90.0f), m_zoomLevel(100.0f), + m_minZoomLevel(10.0f), + m_maxZoomLevel(500.0f), m_wrapXRotation(true), m_wrapYRotation(false), m_activePreset(Q3DCamera::CameraPresetNone) diff --git a/src/datavisualization/engine/q3dcamera.h b/src/datavisualization/engine/q3dcamera.h index a7da9031..5b539ccf 100644 --- a/src/datavisualization/engine/q3dcamera.h +++ b/src/datavisualization/engine/q3dcamera.h @@ -36,6 +36,8 @@ class QT_DATAVISUALIZATION_EXPORT Q3DCamera : public Q3DObject Q_PROPERTY(bool wrapXRotation READ wrapXRotation WRITE setWrapXRotation NOTIFY wrapXRotationChanged) Q_PROPERTY(bool wrapYRotation READ wrapYRotation WRITE setWrapYRotation NOTIFY wrapYRotationChanged) Q_PROPERTY(QVector3D target READ target WRITE setTarget NOTIFY targetChanged REVISION 1) + Q_PROPERTY(float minZoomLevel READ minZoomLevel WRITE setMinZoomLevel NOTIFY minZoomLevelChanged REVISION 1) + Q_PROPERTY(float maxZoomLevel READ maxZoomLevel WRITE setMaxZoomLevel NOTIFY maxZoomLevelChanged REVISION 1) public: enum CameraPreset { @@ -87,6 +89,10 @@ public: float zoomLevel() const; void setZoomLevel(float zoomLevel); + float minZoomLevel() const; + void setMinZoomLevel(float zoomLevel); + float maxZoomLevel() const; + void setMaxZoomLevel(float zoomLevel); void setCameraPosition(float horizontal, float vertical, float zoom = 100.0f); @@ -101,6 +107,8 @@ signals: void wrapXRotationChanged(bool isEnabled); void wrapYRotationChanged(bool isEnabled); Q_REVISION(1) void targetChanged(const QVector3D &target); + Q_REVISION(1) void minZoomLevelChanged(float zoomLevel); + Q_REVISION(1) void maxZoomLevelChanged(float zoomLevel); private: QScopedPointer d_ptr; diff --git a/src/datavisualization/engine/q3dcamera_p.h b/src/datavisualization/engine/q3dcamera_p.h index 6e9fd190..b7826a5b 100644 --- a/src/datavisualization/engine/q3dcamera_p.h +++ b/src/datavisualization/engine/q3dcamera_p.h @@ -90,13 +90,15 @@ public: QMatrix4x4 m_viewMatrix; bool m_isViewMatrixUpdateActive; - GLfloat m_xRotation; - GLfloat m_yRotation; - GLfloat m_minXRotation; - GLfloat m_minYRotation; - GLfloat m_maxXRotation; - GLfloat m_maxYRotation; - GLfloat m_zoomLevel; + float m_xRotation; + float m_yRotation; + float m_minXRotation; + float m_minYRotation; + float m_maxXRotation; + float m_maxYRotation; + float m_zoomLevel; + float m_minZoomLevel; + float m_maxZoomLevel; bool m_wrapXRotation; bool m_wrapYRotation; Q3DCamera::CameraPreset m_activePreset; diff --git a/src/datavisualization/input/q3dinputhandler.cpp b/src/datavisualization/input/q3dinputhandler.cpp index 0bed3cb9..43cee84e 100644 --- a/src/datavisualization/input/q3dinputhandler.cpp +++ b/src/datavisualization/input/q3dinputhandler.cpp @@ -22,18 +22,16 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION -const int minZoomLevel = 10; -const int halfSizeZoomLevel = 50; -const int oneToOneZoomLevel = 100; -const int maxZoomLevel = 500; -const int driftTowardCenterLevel = 175; -const float wheelZoomDrift = 0.1f; +static const int halfSizeZoomLevel = 50; +static const int oneToOneZoomLevel = 100; +static const int driftTowardCenterLevel = 175; +static const float wheelZoomDrift = 0.1f; -const int nearZoomRangeDivider = 12; -const int midZoomRangeDivider = 60; -const int farZoomRangeDivider = 120; +static const int nearZoomRangeDivider = 12; +static const int midZoomRangeDivider = 60; +static const int farZoomRangeDivider = 120; -const float rotationSpeed = 100.0f; +static const float rotationSpeed = 100.0f; /*! * \class Q3DInputHandler @@ -58,7 +56,7 @@ const float rotationSpeed = 100.0f; * \l {QAbstract3DGraph::selectionMode}{selection mode}. * \row * \li Mouse wheel - * \li Zoom in/out within default range (10...500%). + * \li Zoom in/out within the allowable zoom range set for Q3DCamera. * \row * \li Left click on the primary view when the secondary view is visible * \li Closes the secondary view. @@ -239,17 +237,17 @@ void Q3DInputHandler::wheelEvent(QWheelEvent *event) return; // Adjust zoom level based on what zoom range we're in. - int zoomLevel = scene()->activeCamera()->zoomLevel(); + Q3DCamera *camera = scene()->activeCamera(); + int zoomLevel = int(camera->zoomLevel()); + const int minZoomLevel = int(camera->minZoomLevel()); + const int maxZoomLevel = int(camera->maxZoomLevel()); if (zoomLevel > oneToOneZoomLevel) zoomLevel += event->angleDelta().y() / nearZoomRangeDivider; else if (zoomLevel > halfSizeZoomLevel) zoomLevel += event->angleDelta().y() / midZoomRangeDivider; else zoomLevel += event->angleDelta().y() / farZoomRangeDivider; - if (zoomLevel > maxZoomLevel) - zoomLevel = maxZoomLevel; - else if (zoomLevel < minZoomLevel) - zoomLevel = minZoomLevel; + zoomLevel = qBound(minZoomLevel, zoomLevel, maxZoomLevel); if (isZoomAtTargetEnabled()) { scene()->setGraphPositionQuery(event->pos()); @@ -259,7 +257,7 @@ void Q3DInputHandler::wheelEvent(QWheelEvent *event) d_ptr->m_requestedZoomLevel = zoomLevel; d_ptr->m_driftMultiplier = wheelZoomDrift; } else { - scene()->activeCamera()->setZoomLevel(zoomLevel); + camera->setZoomLevel(zoomLevel); } } } diff --git a/src/datavisualization/input/qtouch3dinputhandler.cpp b/src/datavisualization/input/qtouch3dinputhandler.cpp index b811548b..20eebb58 100644 --- a/src/datavisualization/input/qtouch3dinputhandler.cpp +++ b/src/datavisualization/input/qtouch3dinputhandler.cpp @@ -22,18 +22,16 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION -const float maxTapAndHoldJitter = 20.0f; -const int maxPinchJitter = 10; +static const float maxTapAndHoldJitter = 20.0f; +static const int maxPinchJitter = 10; #if defined (Q_OS_ANDROID) || defined(Q_OS_IOS) -const int maxSelectionJitter = 10; +static const int maxSelectionJitter = 10; #else -const int maxSelectionJitter = 5; +static const int maxSelectionJitter = 5; #endif -const int tapAndHoldTime = 250; -const float rotationSpeed = 200.0f; -const int minZoomLevel = 10; -const int maxZoomLevel = 500; -const float touchZoomDrift = 0.02f; +static const int tapAndHoldTime = 250; +static const float rotationSpeed = 200.0f; +static const float touchZoomDrift = 0.02f; /*! * \class QTouch3DInputHandler @@ -61,7 +59,7 @@ const float touchZoomDrift = 0.02f; * \li Same as tap. * \row * \li Pinch - * \li Zoom in/out within default range (10...500%). + * \li Zoom in/out within the allowable zoom range set for Q3DCamera. * \row * \li Tap on the primary view when the secondary view is visible * \li Closes the secondary view. @@ -193,16 +191,15 @@ void QTouch3DInputHandlerPrivate::handlePinchZoom(float distance, const QPoint & return; m_inputState = QAbstract3DInputHandlerPrivate::InputStatePinching; Q3DCamera *camera = q_ptr->scene()->activeCamera(); - int zoomLevel = camera->zoomLevel(); + int zoomLevel = int(camera->zoomLevel()); + const int minZoomLevel = int(camera->minZoomLevel()); + const int maxZoomLevel = int(camera->maxZoomLevel()); float zoomRate = qSqrt(qSqrt(zoomLevel)); if (newDistance > prevDist) zoomLevel += zoomRate; else zoomLevel -= zoomRate; - if (zoomLevel > maxZoomLevel) - zoomLevel = maxZoomLevel; - else if (zoomLevel < minZoomLevel) - zoomLevel = minZoomLevel; + zoomLevel = qBound(minZoomLevel, zoomLevel, maxZoomLevel); if (q_ptr->isZoomAtTargetEnabled()) { q_ptr->scene()->setGraphPositionQuery(pos); -- cgit v1.2.3 From 471bff51a0a066d92c611e680d2bc817ab94736b Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Mon, 29 Sep 2014 10:28:51 +0300 Subject: Make various selection queries thread safe Task-number: QTRD-3333 Change-Id: I09cb9a119a3490de008feb97cbb6f0c623238927 Reviewed-by: Mika Salmela --- .../engine/abstract3dcontroller.cpp | 23 ++++++++++++---------- .../engine/abstract3dcontroller_p.h | 4 ++++ 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/src/datavisualization/engine/abstract3dcontroller.cpp b/src/datavisualization/engine/abstract3dcontroller.cpp index c1d27bab..325cb5ce 100644 --- a/src/datavisualization/engine/abstract3dcontroller.cpp +++ b/src/datavisualization/engine/abstract3dcontroller.cpp @@ -58,7 +58,10 @@ Abstract3DController::Abstract3DController(QRect initialViewport, Q3DScene *scen m_radialLabelOffset(1.0f), m_measureFps(false), m_numFrames(0), - m_currentFps(0.0) + m_currentFps(0.0), + m_clickedType(QAbstract3DGraph::ElementNone), + m_selectedLabelIndex(-1), + m_selectedCustomItemIndex(-1) { if (!m_scene) m_scene = new Q3DScene; @@ -1471,8 +1474,11 @@ void Abstract3DController::emitNeedRender() void Abstract3DController::handlePendingClick() { - QAbstract3DGraph::ElementType type = m_renderer->clickedType(); - emit elementSelected(type); + m_clickedType = m_renderer->clickedType(); + m_selectedLabelIndex = m_renderer->m_selectedLabelIndex; + m_selectedCustomItemIndex = m_renderer->m_selectedCustomItemIndex; + + emit elementSelected(m_clickedType); } void Abstract3DController::handlePendingGraphPositionQuery() @@ -1483,7 +1489,7 @@ void Abstract3DController::handlePendingGraphPositionQuery() int Abstract3DController::selectedLabelIndex() const { - int index = m_renderer->m_selectedLabelIndex; + int index = m_selectedLabelIndex; QAbstract3DAxis *axis = selectedAxis(); if (axis && axis->labels().count() <= index) index = -1; @@ -1493,7 +1499,7 @@ int Abstract3DController::selectedLabelIndex() const QAbstract3DAxis *Abstract3DController::selectedAxis() const { QAbstract3DAxis *axis = 0; - QAbstract3DGraph::ElementType type = m_renderer->clickedType(); + QAbstract3DGraph::ElementType type = m_clickedType; switch (type) { case QAbstract3DGraph::ElementAxisXLabel: axis = axisX(); @@ -1514,7 +1520,7 @@ QAbstract3DAxis *Abstract3DController::selectedAxis() const int Abstract3DController::selectedCustomItemIndex() const { - int index = m_renderer->m_selectedCustomItemIndex; + int index = m_selectedCustomItemIndex; if (m_customItems.count() <= index) index = -1; return index; @@ -1531,10 +1537,7 @@ QCustom3DItem *Abstract3DController::selectedCustomItem() const QAbstract3DGraph::ElementType Abstract3DController::selectedElement() const { - if (m_renderer) - return m_renderer->clickedType(); - else - return QAbstract3DGraph::ElementNone; + return m_clickedType; } void Abstract3DController::setOrthoProjection(bool enable) diff --git a/src/datavisualization/engine/abstract3dcontroller_p.h b/src/datavisualization/engine/abstract3dcontroller_p.h index 125e9885..705de915 100644 --- a/src/datavisualization/engine/abstract3dcontroller_p.h +++ b/src/datavisualization/engine/abstract3dcontroller_p.h @@ -205,6 +205,10 @@ protected: QList m_customItems; + QAbstract3DGraph::ElementType m_clickedType; + int m_selectedLabelIndex; + int m_selectedCustomItemIndex; + explicit Abstract3DController(QRect initialViewport, Q3DScene *scene, QObject *parent = 0); public: -- cgit v1.2.3 From 084010113485262ad4495e0af2d57ad3aa0fcf7e Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Mon, 29 Sep 2014 10:43:48 +0300 Subject: Add C++ API for getting the custom item list from graph. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit QML already had this. Task-number: QTRD-3173 Change-Id: Ib4203eef7a0bc477ddc79467c91b22f95d573154 Reviewed-by: Tomi Korpipää --- src/datavisualization/engine/abstract3dcontroller.cpp | 5 +++++ src/datavisualization/engine/abstract3dcontroller_p.h | 1 + src/datavisualization/engine/qabstract3dgraph.cpp | 12 +++++++++++- src/datavisualization/engine/qabstract3dgraph.h | 1 + 4 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/datavisualization/engine/abstract3dcontroller.cpp b/src/datavisualization/engine/abstract3dcontroller.cpp index 325cb5ce..44f1208b 100644 --- a/src/datavisualization/engine/abstract3dcontroller.cpp +++ b/src/datavisualization/engine/abstract3dcontroller.cpp @@ -1034,6 +1034,11 @@ void Abstract3DController::releaseCustomItem(QCustom3DItem *item) } } +QList Abstract3DController::customItems() const +{ + return m_customItems; +} + void Abstract3DController::updateCustomItem() { m_isCustomItemDirty = true; diff --git a/src/datavisualization/engine/abstract3dcontroller_p.h b/src/datavisualization/engine/abstract3dcontroller_p.h index 705de915..6db774bd 100644 --- a/src/datavisualization/engine/abstract3dcontroller_p.h +++ b/src/datavisualization/engine/abstract3dcontroller_p.h @@ -274,6 +274,7 @@ public: void deleteCustomItem(QCustom3DItem *item); void deleteCustomItem(const QVector3D &position); void releaseCustomItem(QCustom3DItem *item); + QList customItems() const; int selectedLabelIndex() const; QAbstract3DAxis *selectedAxis() const; diff --git a/src/datavisualization/engine/qabstract3dgraph.cpp b/src/datavisualization/engine/qabstract3dgraph.cpp index 0249478f..8473f708 100644 --- a/src/datavisualization/engine/qabstract3dgraph.cpp +++ b/src/datavisualization/engine/qabstract3dgraph.cpp @@ -400,7 +400,7 @@ void QAbstract3DGraph::clearSelection() * \return index to the added item if add was successful, -1 if trying to add a null item, and * index of the item if trying to add an already added item. * - * \sa removeCustomItems(), removeCustomItem(), removeCustomItemAt() + * \sa removeCustomItems(), removeCustomItem(), removeCustomItemAt(), customItems() * * \since QtDataVisualization 1.1 */ @@ -454,6 +454,16 @@ void QAbstract3DGraph::releaseCustomItem(QCustom3DItem *item) return d_ptr->m_visualController->releaseCustomItem(item); } +/*! + * \return list of all added custom items. + * \since QtDataVisualization 1.2 + * \sa addCustomItem() + */ +QList QAbstract3DGraph::customItems() const +{ + return d_ptr->m_visualController->customItems(); +} + /*! * Can be used to query the index of the selected label after receiving \c selectedElementChanged * signal with any label type. Selection is valid until the next \c selectedElementChanged signal. diff --git a/src/datavisualization/engine/qabstract3dgraph.h b/src/datavisualization/engine/qabstract3dgraph.h index 4a6de4f2..79eb3893 100644 --- a/src/datavisualization/engine/qabstract3dgraph.h +++ b/src/datavisualization/engine/qabstract3dgraph.h @@ -134,6 +134,7 @@ public: void removeCustomItem(QCustom3DItem *item); void removeCustomItemAt(const QVector3D &position); void releaseCustomItem(QCustom3DItem *item); + QList customItems() const; int selectedLabelIndex() const; QAbstract3DAxis *selectedAxis() const; -- cgit v1.2.3 From 6821c16e36a7454627ac9a18cd1d8b0f0bd9246f Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Mon, 29 Sep 2014 15:14:25 +0300 Subject: Make background margin configurable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added margin as abstract graph property. If the margin is narrow, the positions of theedge labels of the axes are adjusted to avoid overlap with edge labels on other axes. Task-number: QTRD-3204 Change-Id: I7fa5a04c8f2091519d99689ef2f6cbcf799fb15e Reviewed-by: Mika Salmela Reviewed-by: Tomi Korpipää --- ...tdatavisualization-qml-abstractdeclarative.qdoc | 19 ++++++ .../engine/abstract3dcontroller.cpp | 23 ++++++- .../engine/abstract3dcontroller_p.h | 9 ++- .../engine/abstract3drenderer.cpp | 9 +++ .../engine/abstract3drenderer_p.h | 5 ++ src/datavisualization/engine/bars3drenderer.cpp | 43 ++++++++---- src/datavisualization/engine/bars3drenderer_p.h | 1 + src/datavisualization/engine/qabstract3dgraph.cpp | 31 +++++++++ src/datavisualization/engine/qabstract3dgraph.h | 5 ++ src/datavisualization/engine/scatter3drenderer.cpp | 77 +++++++++++++++++++--- src/datavisualization/engine/scatter3drenderer_p.h | 4 +- src/datavisualization/engine/surface3drenderer.cpp | 72 ++++++++++++++++++-- src/datavisualization/engine/surface3drenderer_p.h | 4 +- src/datavisualizationqml2/abstractdeclarative.cpp | 12 ++++ src/datavisualizationqml2/abstractdeclarative_p.h | 5 ++ tests/barstest/chart.cpp | 6 ++ tests/barstest/chart.h | 1 + tests/barstest/main.cpp | 11 +++- tests/scattertest/main.cpp | 11 +++- tests/scattertest/scatterchart.cpp | 6 ++ tests/scattertest/scatterchart.h | 1 + tests/surfacetest/graphmodifier.cpp | 6 ++ tests/surfacetest/graphmodifier.h | 1 + tests/surfacetest/main.cpp | 11 +++- 24 files changed, 332 insertions(+), 41 deletions(-) diff --git a/src/datavisualization/doc/src/qtdatavisualization-qml-abstractdeclarative.qdoc b/src/datavisualization/doc/src/qtdatavisualization-qml-abstractdeclarative.qdoc index 3dc5beea..b26f0818 100644 --- a/src/datavisualization/doc/src/qtdatavisualization-qml-abstractdeclarative.qdoc +++ b/src/datavisualization/doc/src/qtdatavisualization-qml-abstractdeclarative.qdoc @@ -407,3 +407,22 @@ * \sa Scene3D::graphPositionQuery */ +/*! + * \qmlproperty real AbstractGraph3D::margin + * \since QtDataVisualization 1.2 + * + * This property contains the absolute value used for graph margin. The graph margin is the space + * left between the edge of the plottable graph area and the edge of the graph background. + * If the margin value is negative, the margins are determined automatically and can vary according + * to size of the items in the series and the type of the graph. + * The value is interpreted as a fraction of Y-axis range, provided the graph aspect ratios have + * not beed changed from the defaults. + * Defaults to \c{-1.0}. + * + * \note Having smaller than the automatically determined margin on scatter graph can cause + * the scatter items at the edges of the graph to overlap with the graph background. + * + * \note On scatter and surface graphs, if the margin is comparatively small to the axis label + * size, the positions of the edge labels of the axes are adjusted to avoid overlap with + * the edge labels of the neighboring axes. + */ diff --git a/src/datavisualization/engine/abstract3dcontroller.cpp b/src/datavisualization/engine/abstract3dcontroller.cpp index 44f1208b..27d01029 100644 --- a/src/datavisualization/engine/abstract3dcontroller.cpp +++ b/src/datavisualization/engine/abstract3dcontroller.cpp @@ -61,7 +61,8 @@ Abstract3DController::Abstract3DController(QRect initialViewport, Q3DScene *scen m_currentFps(0.0), m_clickedType(QAbstract3DGraph::ElementNone), m_selectedLabelIndex(-1), - m_selectedCustomItemIndex(-1) + m_selectedCustomItemIndex(-1), + m_margin(-1.0) { if (!m_scene) m_scene = new Q3DScene; @@ -482,6 +483,11 @@ void Abstract3DController::synchDataToRenderer() m_changeTracker.axisZTitleFixedChanged = false; } + if (m_changeTracker.marginChanged) { + m_renderer->updateMargin(float(m_margin)); + m_changeTracker.marginChanged = false; + } + if (m_changedSeriesList.size()) { m_renderer->modifiedSeriesList(m_changedSeriesList); m_changedSeriesList.clear(); @@ -1685,5 +1691,20 @@ QVector3D Abstract3DController::queriedGraphPosition() const return m_queriedGraphPosition; } +void Abstract3DController::setMargin(qreal margin) +{ + if (m_margin != margin) { + m_margin = margin; + m_changeTracker.marginChanged = true; + emit marginChanged(margin); + emitNeedRender(); + } +} + +qreal Abstract3DController::margin() const +{ + return m_margin; +} + QT_END_NAMESPACE_DATAVISUALIZATION diff --git a/src/datavisualization/engine/abstract3dcontroller_p.h b/src/datavisualization/engine/abstract3dcontroller_p.h index 6db774bd..4d7b36a3 100644 --- a/src/datavisualization/engine/abstract3dcontroller_p.h +++ b/src/datavisualization/engine/abstract3dcontroller_p.h @@ -96,6 +96,7 @@ struct Abstract3DChangeBitField { bool radialLabelOffsetChanged : 1; bool reflectionChanged : 1; bool reflectivityChanged : 1; + bool marginChanged : 1; Abstract3DChangeBitField() : themeChanged(true), @@ -144,7 +145,8 @@ struct Abstract3DChangeBitField { polarChanged(true), radialLabelOffsetChanged(true), reflectionChanged(true), - reflectivityChanged(true) + reflectivityChanged(true), + marginChanged(true) { } }; @@ -208,6 +210,7 @@ protected: QAbstract3DGraph::ElementType m_clickedType; int m_selectedLabelIndex; int m_selectedCustomItemIndex; + qreal m_margin; explicit Abstract3DController(QRect initialViewport, Q3DScene *scene, QObject *parent = 0); @@ -310,6 +313,9 @@ public: QVector3D queriedGraphPosition() const; + void setMargin(qreal margin); + qreal margin() const; + void emitNeedRender(); virtual void clearSelection() = 0; @@ -394,6 +400,7 @@ signals: void reflectivityChanged(qreal reflectivity); void localeChanged(const QLocale &locale); void queriedGraphPositionChanged(const QVector3D &data); + void marginChanged(qreal margin); protected: virtual QAbstract3DAxis *createDefaultAxis(QAbstract3DAxis::AxisOrientation orientation); diff --git a/src/datavisualization/engine/abstract3drenderer.cpp b/src/datavisualization/engine/abstract3drenderer.cpp index ab086b5f..aaf5418f 100644 --- a/src/datavisualization/engine/abstract3drenderer.cpp +++ b/src/datavisualization/engine/abstract3drenderer.cpp @@ -92,8 +92,12 @@ Abstract3DRenderer::Abstract3DRenderer(Abstract3DController *controller) m_zRightAngleRotationNeg(QQuaternion::fromAxisAndAngle(0.0f, 0.0f, 1.0f, -90.0f)), m_xFlipRotation(QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, -180.0f)), m_zFlipRotation(QQuaternion::fromAxisAndAngle(0.0f, 0.0f, 1.0f, -180.0f)), + m_requestedMargin(-1.0f), m_vBackgroundMargin(0.1f), m_hBackgroundMargin(0.1f), + m_scaleXWithBackground(0.0f), + m_scaleYWithBackground(0.0f), + m_scaleZWithBackground(0.0f), m_oldCameraTarget(QVector3D(2000.0f, 2000.0f, 2000.0f)), // Just random invalid target m_reflectionEnabled(false), m_reflectivity(0.5) @@ -459,6 +463,11 @@ void Abstract3DRenderer::updateRadialLabelOffset(float offset) m_radialLabelOffset = offset; } +void Abstract3DRenderer::updateMargin(float margin) +{ + m_requestedMargin = margin; +} + void Abstract3DRenderer::updateOptimizationHint(QAbstract3DGraph::OptimizationHints hint) { m_cachedOptimizationHint = hint; diff --git a/src/datavisualization/engine/abstract3drenderer_p.h b/src/datavisualization/engine/abstract3drenderer_p.h index 6d134ee3..997362d5 100644 --- a/src/datavisualization/engine/abstract3drenderer_p.h +++ b/src/datavisualization/engine/abstract3drenderer_p.h @@ -136,6 +136,7 @@ public: virtual void updateHorizontalAspectRatio(float ratio); virtual void updatePolar(bool enable); virtual void updateRadialLabelOffset(float offset); + virtual void updateMargin(float margin); virtual QVector3D convertPositionToTranslation(const QVector3D &position, bool isAbsolute) = 0; @@ -297,8 +298,12 @@ protected: QQuaternion m_xFlipRotation; QQuaternion m_zFlipRotation; + float m_requestedMargin; float m_vBackgroundMargin; float m_hBackgroundMargin; + float m_scaleXWithBackground; + float m_scaleYWithBackground; + float m_scaleZWithBackground; QVector3D m_oldCameraTarget; diff --git a/src/datavisualization/engine/bars3drenderer.cpp b/src/datavisualization/engine/bars3drenderer.cpp index 2d394d57..47c9b84c 100644 --- a/src/datavisualization/engine/bars3drenderer.cpp +++ b/src/datavisualization/engine/bars3drenderer.cpp @@ -1702,7 +1702,8 @@ void Bars3DRenderer::drawBackground(GLfloat backgroundRotation, QMatrix4x4 MVPMatrix; QMatrix4x4 itModelMatrix; - QVector3D backgroundScaler(m_xScaleFactor, 1.0f, m_zScaleFactor); + QVector3D backgroundScaler(m_scaleXWithBackground, m_scaleYWithBackground, + m_scaleZWithBackground); QVector4D backgroundColor = Utils::vectorFromColor(m_cachedTheme->backgroundColor()); if (m_reflectionEnabled) backgroundColor.setW(backgroundColor.w() * m_reflectivity); @@ -1845,7 +1846,7 @@ void Bars3DRenderer::drawGridLines(const QMatrix4x4 &depthProjectionViewMatrix, if (m_yFlipped) yFloorLinePosition = -yFloorLinePosition; - QVector3D gridLineScaler(m_xScaleFactor, gridLineWidth, gridLineWidth); + QVector3D gridLineScaler(m_scaleXWithBackground, gridLineWidth, gridLineWidth); if (m_yFlipped) lineRotation = m_xRightAngleRotation; @@ -1894,7 +1895,7 @@ void Bars3DRenderer::drawGridLines(const QMatrix4x4 &depthProjectionViewMatrix, #if defined(QT_OPENGL_ES_2) lineRotation = m_yRightAngleRotation; #endif - gridLineScaler = QVector3D(gridLineWidth, gridLineWidth, m_zScaleFactor); + gridLineScaler = QVector3D(gridLineWidth, gridLineWidth, m_scaleZWithBackground); for (GLfloat bar = 0.0f; bar <= m_cachedColumnCount; bar++) { QMatrix4x4 modelMatrix; QMatrix4x4 MVPMatrix; @@ -1936,11 +1937,11 @@ void Bars3DRenderer::drawGridLines(const QMatrix4x4 &depthProjectionViewMatrix, // Wall lines: back wall int gridLineCount = m_axisCacheY.gridLineCount(); - GLfloat zWallLinePosition = -m_zScaleFactor + gridLineOffset; + GLfloat zWallLinePosition = -m_scaleZWithBackground + gridLineOffset; if (m_zFlipped) zWallLinePosition = -zWallLinePosition; - gridLineScaler = QVector3D(m_xScaleFactor, gridLineWidth, gridLineWidth); + gridLineScaler = QVector3D(m_scaleXWithBackground, gridLineWidth, gridLineWidth); for (int line = 0; line < gridLineCount; line++) { QMatrix4x4 modelMatrix; QMatrix4x4 MVPMatrix; @@ -1981,7 +1982,7 @@ void Bars3DRenderer::drawGridLines(const QMatrix4x4 &depthProjectionViewMatrix, } // Wall lines: side wall - GLfloat xWallLinePosition = -m_xScaleFactor + gridLineOffset; + GLfloat xWallLinePosition = -m_scaleXWithBackground + gridLineOffset; if (m_xFlipped) xWallLinePosition = -xWallLinePosition; @@ -1990,7 +1991,7 @@ void Bars3DRenderer::drawGridLines(const QMatrix4x4 &depthProjectionViewMatrix, else lineRotation = m_yRightAngleRotation; - gridLineScaler = QVector3D(gridLineWidth, gridLineWidth, m_zScaleFactor); + gridLineScaler = QVector3D(gridLineWidth, gridLineWidth, m_scaleZWithBackground); for (int line = 0; line < gridLineCount; line++) { QMatrix4x4 modelMatrix; QMatrix4x4 MVPMatrix; @@ -2064,8 +2065,8 @@ void Bars3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCamer int labelCount = m_axisCacheY.labelCount(); GLfloat labelMarginXTrans = labelMargin; GLfloat labelMarginZTrans = labelMargin; - GLfloat labelXTrans = m_xScaleFactor; - GLfloat labelZTrans = m_zScaleFactor; + GLfloat labelXTrans = m_scaleXWithBackground; + GLfloat labelZTrans = m_scaleZWithBackground; QVector3D backLabelRotation(0.0f, -90.0f, 0.0f); QVector3D sideLabelRotation(0.0f, 0.0f, 0.0f); Qt::AlignmentFlag backAlignment = (m_xFlipped != m_zFlipped) ? Qt::AlignLeft : Qt::AlignRight; @@ -2169,10 +2170,8 @@ void Bars3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCamer fractionCamY = activeCamera->yRotation() * labelAngleFraction; fractionCamX = activeCamera->xRotation() * labelAngleFraction; GLfloat labelYAdjustment = 0.005f; - GLfloat scaledRowWidth = m_xScaleFactor; - GLfloat scaledColumnDepth = m_zScaleFactor; - GLfloat colPosValue = scaledRowWidth + labelMargin; - GLfloat rowPosValue = scaledColumnDepth + labelMargin; + GLfloat colPosValue = m_scaleXWithBackground + labelMargin; + GLfloat rowPosValue = m_scaleZWithBackground + labelMargin; GLfloat rowPos = 0.0f; GLfloat colPos = 0.0f; Qt::AlignmentFlag alignment = (m_xFlipped == m_zFlipped) ? Qt::AlignLeft : Qt::AlignRight; @@ -2599,6 +2598,18 @@ void Bars3DRenderer::calculateSceneScalingFactors() m_xScaleFactor = m_rowWidth / m_scaleFactor; m_zScaleFactor = m_columnDepth / m_scaleFactor; + if (m_requestedMargin < 0.0f) { + m_hBackgroundMargin = 0.0f; + m_vBackgroundMargin = 0.0f; + } else { + m_hBackgroundMargin = m_requestedMargin; + m_vBackgroundMargin = m_requestedMargin; + } + + m_scaleXWithBackground = m_xScaleFactor + m_hBackgroundMargin; + m_scaleYWithBackground = 1.0f + m_vBackgroundMargin; + m_scaleZWithBackground = m_zScaleFactor + m_hBackgroundMargin; + updateCameraViewport(); updateCustomItemPositions(); } @@ -2858,4 +2869,10 @@ void Bars3DRenderer::updateFloorLevel(float level) calculateHeightAdjustment(); } +void Bars3DRenderer::updateMargin(float margin) +{ + Abstract3DRenderer::updateMargin(margin); + calculateSceneScalingFactors(); +} + QT_END_NAMESPACE_DATAVISUALIZATION diff --git a/src/datavisualization/engine/bars3drenderer_p.h b/src/datavisualization/engine/bars3drenderer_p.h index 92dae311..fea4e52e 100644 --- a/src/datavisualization/engine/bars3drenderer_p.h +++ b/src/datavisualization/engine/bars3drenderer_p.h @@ -118,6 +118,7 @@ public: void updateAspectRatio(float ratio); void updateFloorLevel(float level); + void updateMargin(float margin); protected: virtual void initializeOpenGL(); diff --git a/src/datavisualization/engine/qabstract3dgraph.cpp b/src/datavisualization/engine/qabstract3dgraph.cpp index 8473f708..9fb71d59 100644 --- a/src/datavisualization/engine/qabstract3dgraph.cpp +++ b/src/datavisualization/engine/qabstract3dgraph.cpp @@ -820,6 +820,35 @@ QVector3D QAbstract3DGraph::queriedGraphPosition() const return d_ptr->m_visualController->queriedGraphPosition(); } +/*! + * \property QAbstract3DGraph::margin + * \since QtDataVisualization 1.2 + * + * This property contains the absolute value used for graph margin. The graph margin is the space + * left between the edge of the plottable graph area and the edge of the graph background. + * If the margin value is negative, the margins are determined automatically and can vary according + * to size of the items in the series and the type of the graph. + * The value is interpreted as a fraction of Y-axis range, provided the graph aspect ratios have + * not beed changed from the defaults. + * Defaults to \c{-1.0}. + * + * \note Having smaller than the automatically determined margin on scatter graph can cause + * the scatter items at the edges of the graph to overlap with the graph background. + * + * \note On scatter and surface graphs, if the margin is comparatively small to the axis label + * size, the positions of the edge labels of the axes are adjusted to avoid overlap with + * the edge labels of the neighboring axes. + */ +void QAbstract3DGraph::setMargin(qreal margin) +{ + d_ptr->m_visualController->setMargin(margin); +} + +qreal QAbstract3DGraph::margin() const +{ + return d_ptr->m_visualController->margin(); +} + /*! * \internal */ @@ -985,6 +1014,8 @@ void QAbstract3DGraphPrivate::setVisualController(Abstract3DController *controll &QAbstract3DGraph::localeChanged); QObject::connect(m_visualController, &Abstract3DController::queriedGraphPositionChanged, q_ptr, &QAbstract3DGraph::queriedGraphPositionChanged); + QObject::connect(m_visualController, &Abstract3DController::marginChanged, q_ptr, + &QAbstract3DGraph::marginChanged); } void QAbstract3DGraphPrivate::handleDevicePixelRatioChange() diff --git a/src/datavisualization/engine/qabstract3dgraph.h b/src/datavisualization/engine/qabstract3dgraph.h index 79eb3893..a0c26a42 100644 --- a/src/datavisualization/engine/qabstract3dgraph.h +++ b/src/datavisualization/engine/qabstract3dgraph.h @@ -58,6 +58,7 @@ class QT_DATAVISUALIZATION_EXPORT QAbstract3DGraph : public QWindow, protected Q Q_PROPERTY(qreal reflectivity READ reflectivity WRITE setReflectivity NOTIFY reflectivityChanged) Q_PROPERTY(QLocale locale READ locale WRITE setLocale NOTIFY localeChanged) Q_PROPERTY(QVector3D queriedGraphPosition READ queriedGraphPosition NOTIFY queriedGraphPositionChanged) + Q_PROPERTY(qreal margin READ margin WRITE setMargin NOTIFY marginChanged) protected: explicit QAbstract3DGraph(QAbstract3DGraphPrivate *d, const QSurfaceFormat *format, @@ -179,6 +180,9 @@ public: QVector3D queriedGraphPosition() const; + void setMargin(qreal margin); + qreal margin() const; + protected: bool event(QEvent *event); void resizeEvent(QResizeEvent *event); @@ -209,6 +213,7 @@ signals: void reflectivityChanged(qreal reflectivity); void localeChanged(const QLocale &locale); void queriedGraphPositionChanged(const QVector3D &data); + void marginChanged(qreal margin); private: Q_DISABLE_COPY(QAbstract3DGraph) diff --git a/src/datavisualization/engine/scatter3drenderer.cpp b/src/datavisualization/engine/scatter3drenderer.cpp index d1dca8c6..3bae9879 100644 --- a/src/datavisualization/engine/scatter3drenderer.cpp +++ b/src/datavisualization/engine/scatter3drenderer.cpp @@ -60,9 +60,6 @@ Scatter3DRenderer::Scatter3DRenderer(Scatter3DController *controller) m_scaleX(0.0f), m_scaleY(0.0f), m_scaleZ(0.0f), - m_scaleXWithBackground(0.0f), - m_scaleYWithBackground(0.0f), - m_scaleZWithBackground(0.0f), m_selectedItemIndex(Scatter3DController::invalidSelectionIndex()), m_selectedSeriesCache(0), m_oldSelectedSeriesCache(0), @@ -399,6 +396,12 @@ void Scatter3DRenderer::updateOptimizationHint(QAbstract3DGraph::OptimizationHin #endif } +void Scatter3DRenderer::updateMargin(float margin) +{ + Abstract3DRenderer::updateMargin(margin); + calculateSceneScalingFactors(); +} + void Scatter3DRenderer::resetClickedStatus() { m_clickedIndex = Scatter3DController::invalidSelectionIndex(); @@ -1689,6 +1692,23 @@ void Scatter3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa } else { labelTrans.setZ(m_axisCacheZ.labelPosition(label)); } + if (label == 0 || label == (labelCount - 1)) { + // If the margin is small, adjust the position of the edge labels to avoid overlapping + // with labels of the other axes. + float scaleFactor = m_drawer->scaledFontSize() / axisLabelItem.size().height(); + float labelOverlap = qAbs(labelTrans.z()) + + (scaleFactor * axisLabelItem.size().height() / 2.0f) + - m_scaleZWithBackground + labelMargin; + // No need to adjust quite as much on the front edges + if (label != startIndex) + labelOverlap /= 2.0f; + if (labelOverlap > 0.0f) { + if (label == 0) + labelTrans.setZ(labelTrans.z() - labelOverlap); + else + labelTrans.setZ(labelTrans.z() + labelOverlap); + } + } m_dummyRenderItem.setTranslation(labelTrans); if (drawSelection) { @@ -1860,8 +1880,25 @@ void Scatter3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa } else { labelTrans.setX(m_axisCacheX.labelPosition(label)); } - m_dummyRenderItem.setTranslation(labelTrans); const LabelItem &axisLabelItem = *m_axisCacheX.labelItems().at(label); + if (label == 0 || label == (labelCount - 1)) { + // If the margin is small, adjust the position of the edge labels to avoid overlapping + // with labels of the other axes. + float scaleFactor = m_drawer->scaledFontSize() / axisLabelItem.size().height(); + float labelOverlap = qAbs(labelTrans.x()) + + (scaleFactor * axisLabelItem.size().height() / 2.0f) + - m_scaleXWithBackground + labelMargin; + // No need to adjust quite as much on the front edges + if (label != startIndex) + labelOverlap /= 2.0f; + if (labelOverlap > 0.0f) { + if (label == 0) + labelTrans.setX(labelTrans.x() + labelOverlap); + else + labelTrans.setX(labelTrans.x() - labelOverlap); + } + } + m_dummyRenderItem.setTranslation(labelTrans); if (drawSelection) { QVector4D labelColor = QVector4D(0.0f, label / 255.0f, 0.0f, @@ -1965,7 +2002,7 @@ void Scatter3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa float offsetValue = 0.0f; for (int label = startIndex; label != endIndex; label = label + indexStep) { const LabelItem &axisLabelItem = *m_axisCacheY.labelItems().at(label); - const float labelYTrans = m_axisCacheY.labelPosition(label); + float labelYTrans = m_axisCacheY.labelPosition(label); glPolygonOffset(offsetValue++ / -10.0f, 1.0f); @@ -1975,6 +2012,21 @@ void Scatter3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa shader->setUniformValue(shader->color(), labelColor); } + if (label == startIndex) { + // If the margin is small, adjust the position of the edge label to avoid + // overlapping with labels of the other axes. + float scaleFactor = m_drawer->scaledFontSize() / axisLabelItem.size().height(); + float labelOverlap = qAbs(labelYTrans) + + (scaleFactor * axisLabelItem.size().height() / 2.0f) + - m_scaleYWithBackground + labelMargin; + if (labelOverlap > 0.0f) { + if (label == 0) + labelYTrans += labelOverlap; + else + labelYTrans -= labelOverlap; + } + } + // Back wall labelTransBack.setY(labelYTrans); m_dummyRenderItem.setTranslation(labelTransBack); @@ -2121,11 +2173,16 @@ void Scatter3DRenderer::calculateTranslation(ScatterRenderItem &item) void Scatter3DRenderer::calculateSceneScalingFactors() { - if (m_maxItemSize > defaultMaxSize) - m_hBackgroundMargin = m_maxItemSize / itemScaler; - else - m_hBackgroundMargin = defaultMaxSize; - m_vBackgroundMargin = m_hBackgroundMargin; + if (m_requestedMargin < 0.0f) { + if (m_maxItemSize > defaultMaxSize) + m_hBackgroundMargin = m_maxItemSize / itemScaler; + else + m_hBackgroundMargin = defaultMaxSize; + m_vBackgroundMargin = m_hBackgroundMargin; + } else { + m_hBackgroundMargin = m_requestedMargin; + m_vBackgroundMargin = m_requestedMargin; + } if (m_polarGraph) { float polarMargin = calculatePolarBackgroundMargin(); m_hBackgroundMargin = qMax(m_hBackgroundMargin, polarMargin); diff --git a/src/datavisualization/engine/scatter3drenderer_p.h b/src/datavisualization/engine/scatter3drenderer_p.h index d8908e59..d882d59f 100644 --- a/src/datavisualization/engine/scatter3drenderer_p.h +++ b/src/datavisualization/engine/scatter3drenderer_p.h @@ -70,9 +70,6 @@ private: float m_scaleX; float m_scaleY; float m_scaleZ; - float m_scaleXWithBackground; - float m_scaleYWithBackground; - float m_scaleZWithBackground; int m_selectedItemIndex; ScatterSeriesRenderCache *m_selectedSeriesCache; ScatterSeriesRenderCache *m_oldSelectedSeriesCache; @@ -99,6 +96,7 @@ public: void updateAxisTitleVisibility(QAbstract3DAxis::AxisOrientation orientation, bool visible); void updateOptimizationHint(QAbstract3DGraph::OptimizationHints hint); + void updateMargin(float margin); QVector3D convertPositionToTranslation(const QVector3D &position, bool isAbsolute); diff --git a/src/datavisualization/engine/surface3drenderer.cpp b/src/datavisualization/engine/surface3drenderer.cpp index 3d39a71d..02add48f 100644 --- a/src/datavisualization/engine/surface3drenderer.cpp +++ b/src/datavisualization/engine/surface3drenderer.cpp @@ -53,9 +53,6 @@ Surface3DRenderer::Surface3DRenderer(Surface3DController *controller) m_scaleX(0.0f), m_scaleY(0.0f), m_scaleZ(0.0f), - m_scaleXWithBackground(0.0f), - m_scaleYWithBackground(0.0f), - m_scaleZWithBackground(0.0f), m_depthModelTexture(0), m_depthFrameBuffer(0), m_selectionFrameBuffer(0), @@ -2089,6 +2086,23 @@ void Surface3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa } else { labelTrans.setZ(m_axisCacheZ.labelPosition(label)); } + if (label == 0 || label == (labelCount - 1)) { + // If the margin is small, adjust the position of the edge labels to avoid overlapping + // with labels of the other axes. + float scaleFactor = m_drawer->scaledFontSize() / axisLabelItem.size().height(); + float labelOverlap = qAbs(labelTrans.z()) + + (scaleFactor * axisLabelItem.size().height() / 2.0f) + - m_scaleZWithBackground + labelMargin; + // No need to adjust quite as much on the front edges + if (label != startIndex) + labelOverlap /= 2.0f; + if (labelOverlap > 0.0f) { + if (label == 0) + labelTrans.setZ(labelTrans.z() - labelOverlap); + else + labelTrans.setZ(labelTrans.z() + labelOverlap); + } + } m_dummyRenderItem.setTranslation(labelTrans); if (drawSelection) { @@ -2264,8 +2278,25 @@ void Surface3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa } else { labelTrans.setX(m_axisCacheX.labelPosition(label)); } - m_dummyRenderItem.setTranslation(labelTrans); const LabelItem &axisLabelItem = *m_axisCacheX.labelItems().at(label); + if (label == 0 || label == (labelCount - 1)) { + // If the margin is small, adjust the position of the edge labels to avoid overlapping + // with labels of the other axes. + float scaleFactor = m_drawer->scaledFontSize() / axisLabelItem.size().height(); + float labelOverlap = qAbs(labelTrans.x()) + + (scaleFactor * axisLabelItem.size().height() / 2.0f) + - m_scaleXWithBackground + labelMargin; + // No need to adjust quite as much on the front edges + if (label != startIndex) + labelOverlap /= 2.0f; + if (labelOverlap > 0.0f) { + if (label == 0) + labelTrans.setX(labelTrans.x() + labelOverlap); + else + labelTrans.setX(labelTrans.x() - labelOverlap); + } + } + m_dummyRenderItem.setTranslation(labelTrans); if (drawSelection) { QVector4D labelColor = QVector4D(0.0f, label / 255.0f, 0.0f, @@ -2368,7 +2399,7 @@ void Surface3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa float offsetValue = 0.0f; for (int label = startIndex; label != endIndex; label = label + indexStep) { const LabelItem &axisLabelItem = *m_axisCacheY.labelItems().at(label); - const float labelYTrans = m_axisCacheY.labelPosition(label); + float labelYTrans = m_axisCacheY.labelPosition(label); glPolygonOffset(offsetValue++ / -10.0f, 1.0f); @@ -2378,6 +2409,21 @@ void Surface3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa shader->setUniformValue(shader->color(), labelColor); } + if (label == startIndex) { + // If the margin is small, adjust the position of the edge label to avoid + // overlapping with labels of the other axes. + float scaleFactor = m_drawer->scaledFontSize() / axisLabelItem.size().height(); + float labelOverlap = qAbs(labelYTrans) + + (scaleFactor * axisLabelItem.size().height() / 2.0f) + - m_scaleYWithBackground + labelMargin; + if (labelOverlap > 0.0f) { + if (label == 0) + labelYTrans += labelOverlap; + else + labelYTrans -= labelOverlap; + } + } + // Back wall labelTransBack.setY(labelYTrans); m_dummyRenderItem.setTranslation(labelTransBack); @@ -2514,9 +2560,15 @@ void Surface3DRenderer::idToRGBA(uint id, uchar *r, uchar *g, uchar *b, uchar *a void Surface3DRenderer::calculateSceneScalingFactors() { - // Margin for background (0.10 make it 10% larger to avoid + // Margin for background (the default 0.10 makes it 10% larger to avoid // selection ball being drawn inside background) - m_hBackgroundMargin = 0.1f; + if (m_requestedMargin < 0.0f) { + m_hBackgroundMargin = 0.1f; + m_vBackgroundMargin = 0.1f; + } else { + m_hBackgroundMargin = m_requestedMargin; + m_vBackgroundMargin = m_requestedMargin; + } if (m_polarGraph) { float polarMargin = calculatePolarBackgroundMargin(); m_hBackgroundMargin = qMax(m_hBackgroundMargin, polarMargin); @@ -3032,4 +3084,10 @@ void Surface3DRenderer::updateAxisTitleVisibility(QAbstract3DAxis::AxisOrientati calculateSceneScalingFactors(); } +void Surface3DRenderer::updateMargin(float margin) +{ + Abstract3DRenderer::updateMargin(margin); + calculateSceneScalingFactors(); +} + QT_END_NAMESPACE_DATAVISUALIZATION diff --git a/src/datavisualization/engine/surface3drenderer_p.h b/src/datavisualization/engine/surface3drenderer_p.h index cba1b959..b02bd456 100644 --- a/src/datavisualization/engine/surface3drenderer_p.h +++ b/src/datavisualization/engine/surface3drenderer_p.h @@ -61,9 +61,6 @@ private: float m_scaleX; float m_scaleY; float m_scaleZ; - float m_scaleXWithBackground; - float m_scaleYWithBackground; - float m_scaleZWithBackground; GLuint m_depthModelTexture; GLuint m_depthFrameBuffer; GLuint m_selectionFrameBuffer; @@ -104,6 +101,7 @@ public: const QStringList &labels); void updateAxisTitleVisibility(QAbstract3DAxis::AxisOrientation orientation, bool visible); + void updateMargin(float margin); void render(GLuint defaultFboHandle = 0); diff --git a/src/datavisualizationqml2/abstractdeclarative.cpp b/src/datavisualizationqml2/abstractdeclarative.cpp index 9206973a..6c6cdb90 100644 --- a/src/datavisualizationqml2/abstractdeclarative.cpp +++ b/src/datavisualizationqml2/abstractdeclarative.cpp @@ -343,6 +343,8 @@ void AbstractDeclarative::setSharedController(Abstract3DController *controller) &AbstractDeclarative::localeChanged); QObject::connect(m_controller.data(), &Abstract3DController::queriedGraphPositionChanged, this, &AbstractDeclarative::queriedGraphPositionChanged); + QObject::connect(m_controller.data(), &Abstract3DController::marginChanged, this, + &AbstractDeclarative::marginChanged); } void AbstractDeclarative::activateOpenGLContext(QQuickWindow *window) @@ -803,6 +805,16 @@ QVector3D AbstractDeclarative::queriedGraphPosition() const return m_controller->queriedGraphPosition(); } +void AbstractDeclarative::setMargin(qreal margin) +{ + m_controller->setMargin(margin); +} + +qreal AbstractDeclarative::margin() const +{ + return m_controller->margin(); +} + void AbstractDeclarative::windowDestroyed(QObject *obj) { // Remove destroyed window from window lists diff --git a/src/datavisualizationqml2/abstractdeclarative_p.h b/src/datavisualizationqml2/abstractdeclarative_p.h index 8dc260c2..04db21f4 100644 --- a/src/datavisualizationqml2/abstractdeclarative_p.h +++ b/src/datavisualizationqml2/abstractdeclarative_p.h @@ -79,6 +79,7 @@ class AbstractDeclarative : public QQuickItem Q_PROPERTY(qreal reflectivity READ reflectivity WRITE setReflectivity NOTIFY reflectivityChanged REVISION 2) Q_PROPERTY(QLocale locale READ locale WRITE setLocale NOTIFY localeChanged REVISION 2) Q_PROPERTY(QVector3D queriedGraphPosition READ queriedGraphPosition NOTIFY queriedGraphPositionChanged REVISION 2) + Q_PROPERTY(qreal margin READ margin WRITE setMargin NOTIFY marginChanged REVISION 2) public: enum SelectionFlag { @@ -220,6 +221,9 @@ public: QVector3D queriedGraphPosition() const; + void setMargin(qreal margin); + qreal margin() const; + public slots: virtual void handleAxisXChanged(QAbstract3DAxis *axis) = 0; virtual void handleAxisYChanged(QAbstract3DAxis *axis) = 0; @@ -264,6 +268,7 @@ signals: Q_REVISION(2) void reflectivityChanged(qreal reflectivity); Q_REVISION(2) void localeChanged(const QLocale &locale); Q_REVISION(2) void queriedGraphPositionChanged(const QVector3D &data); + Q_REVISION(2) void marginChanged(qreal margin); private: QPointer m_controller; diff --git a/tests/barstest/chart.cpp b/tests/barstest/chart.cpp index d7b23719..6be5e66d 100644 --- a/tests/barstest/chart.cpp +++ b/tests/barstest/chart.cpp @@ -1531,6 +1531,12 @@ void GraphModifier::setFloorLevel(int value) qDebug() << "Floor level:" << value; } +void GraphModifier::setGraphMargin(int value) +{ + m_graph->setMargin(qreal(value) / 100.0); + qDebug() << "Setting margin:" << m_graph->margin() << value; +} + void GraphModifier::populateFlatSeries(QBar3DSeries *series, int rows, int columns, float value) { QBarDataArray *dataArray = new QBarDataArray; diff --git a/tests/barstest/chart.h b/tests/barstest/chart.h index 93abbfa4..9713d06c 100644 --- a/tests/barstest/chart.h +++ b/tests/barstest/chart.h @@ -126,6 +126,7 @@ public slots: void setCameraTargetY(int value); void setCameraTargetZ(int value); void setFloorLevel(int value); + void setGraphMargin(int value); signals: void shadowQualityChanged(int quality); diff --git a/tests/barstest/main.cpp b/tests/barstest/main.cpp index cffbf4b4..a468cb57 100644 --- a/tests/barstest/main.cpp +++ b/tests/barstest/main.cpp @@ -368,6 +368,11 @@ int main(int argc, char **argv) cameraTargetSliderZ->setValue(0); cameraTargetSliderZ->setMaximum(100); + QSlider *marginSlider = new QSlider(Qt::Horizontal, widget); + marginSlider->setMinimum(-1); + marginSlider->setValue(-1); + marginSlider->setMaximum(100); + vLayout->addWidget(addSeriesButton, 0, Qt::AlignTop); vLayout->addWidget(addDataButton, 0, Qt::AlignTop); vLayout->addWidget(addMultiDataButton, 0, Qt::AlignTop); @@ -447,7 +452,9 @@ int main(int argc, char **argv) vLayout3->addWidget(reflectivitySlider, 0, Qt::AlignTop); vLayout3->addWidget(toggleCustomItemButton, 0, Qt::AlignTop); vLayout3->addWidget(new QLabel(QStringLiteral("Adjust floor level")), 0, Qt::AlignTop); - vLayout3->addWidget(floorLevelSlider, 1, Qt::AlignTop); + vLayout3->addWidget(floorLevelSlider, 0, Qt::AlignTop); + vLayout3->addWidget(new QLabel(QStringLiteral("Adjust margin")), 0, Qt::AlignTop); + vLayout3->addWidget(marginSlider, 1, Qt::AlignTop); widget->show(); @@ -584,6 +591,8 @@ int main(int argc, char **argv) &GraphModifier::setReflectivity); QObject::connect(floorLevelSlider, &QSlider::valueChanged, modifier, &GraphModifier::setFloorLevel); + QObject::connect(marginSlider, &QSlider::valueChanged, modifier, + &GraphModifier::setGraphMargin); QObject::connect(toggleCustomItemButton, &QPushButton::clicked, modifier, &GraphModifier::toggleCustomItem); diff --git a/tests/scattertest/main.cpp b/tests/scattertest/main.cpp index 116a4dc2..811c7f3b 100644 --- a/tests/scattertest/main.cpp +++ b/tests/scattertest/main.cpp @@ -294,6 +294,11 @@ int main(int argc, char **argv) cameraTargetSliderZ->setValue(0); cameraTargetSliderZ->setMaximum(100); + QSlider *marginSlider = new QSlider(Qt::Horizontal, widget); + marginSlider->setMinimum(-1); + marginSlider->setValue(-1); + marginSlider->setMaximum(100); + vLayout->addWidget(themeButton, 0, Qt::AlignTop); vLayout->addWidget(labelButton, 0, Qt::AlignTop); vLayout->addWidget(styleButton, 0, Qt::AlignTop); @@ -357,7 +362,9 @@ int main(int argc, char **argv) vLayout3->addWidget(new QLabel(QStringLiteral("Camera target")), 0, Qt::AlignTop); vLayout3->addWidget(cameraTargetSliderX, 0, Qt::AlignTop); vLayout3->addWidget(cameraTargetSliderY, 0, Qt::AlignTop); - vLayout3->addWidget(cameraTargetSliderZ, 1, Qt::AlignTop); + vLayout3->addWidget(cameraTargetSliderZ, 0, Qt::AlignTop); + vLayout3->addWidget(new QLabel(QStringLiteral("Adjust margin")), 0, Qt::AlignTop); + vLayout3->addWidget(marginSlider, 1, Qt::AlignTop); ScatterDataModifier *modifier = new ScatterDataModifier(chart); @@ -469,6 +476,8 @@ int main(int argc, char **argv) &ScatterDataModifier::setCameraTargetY); QObject::connect(cameraTargetSliderZ, &QSlider::valueChanged, modifier, &ScatterDataModifier::setCameraTargetZ); + QObject::connect(marginSlider, &QSlider::valueChanged, modifier, + &ScatterDataModifier::setGraphMargin); modifier->setFpsLabel(fpsLabel); diff --git a/tests/scattertest/scatterchart.cpp b/tests/scattertest/scatterchart.cpp index a45c090b..c7b93c32 100644 --- a/tests/scattertest/scatterchart.cpp +++ b/tests/scattertest/scatterchart.cpp @@ -1023,6 +1023,12 @@ void ScatterDataModifier::setCameraTargetZ(int value) qDebug() << "m_cameraTarget:" << m_cameraTarget; } +void ScatterDataModifier::setGraphMargin(int value) +{ + m_chart->setMargin(qreal(value) / 100.0); + qDebug() << "Setting margin:" << m_chart->margin() << value; +} + void ScatterDataModifier::changeShadowQuality(int quality) { QAbstract3DGraph::ShadowQuality sq = QAbstract3DGraph::ShadowQuality(quality); diff --git a/tests/scattertest/scatterchart.h b/tests/scattertest/scatterchart.h index 1a97c5f0..a2b0f58e 100644 --- a/tests/scattertest/scatterchart.h +++ b/tests/scattertest/scatterchart.h @@ -101,6 +101,7 @@ public slots: void setCameraTargetX(int value); void setCameraTargetY(int value); void setCameraTargetZ(int value); + void setGraphMargin(int value); signals: void shadowQualityChanged(int quality); diff --git a/tests/surfacetest/graphmodifier.cpp b/tests/surfacetest/graphmodifier.cpp index 1a069cf4..2d0e9357 100644 --- a/tests/surfacetest/graphmodifier.cpp +++ b/tests/surfacetest/graphmodifier.cpp @@ -836,6 +836,12 @@ void GraphModifier::setCameraTargetZ(int value) qDebug() << "m_cameraTarget:" << m_cameraTarget; } +void GraphModifier::setGraphMargin(int value) +{ + m_graph->setMargin(qreal(value) / 100.0); + qDebug() << "Setting margin:" << m_graph->margin() << value; +} + void GraphModifier::resetArrayAndSliders(QSurfaceDataArray *array, float minZ, float maxZ, float minX, float maxX) { m_axisMinSliderX->setValue(minX); diff --git a/tests/surfacetest/graphmodifier.h b/tests/surfacetest/graphmodifier.h index d1c77940..f85fbc15 100644 --- a/tests/surfacetest/graphmodifier.h +++ b/tests/surfacetest/graphmodifier.h @@ -137,6 +137,7 @@ public slots: void setCameraTargetX(int value); void setCameraTargetY(int value); void setCameraTargetZ(int value); + void setGraphMargin(int value); private: void fillSeries(); diff --git a/tests/surfacetest/main.cpp b/tests/surfacetest/main.cpp index 9e14c665..d1328e4e 100644 --- a/tests/surfacetest/main.cpp +++ b/tests/surfacetest/main.cpp @@ -425,6 +425,11 @@ int main(int argc, char *argv[]) cameraTargetSliderZ->setValue(0); cameraTargetSliderZ->setMaximum(100); + QSlider *marginSlider = new QSlider(Qt::Horizontal, widget); + marginSlider->setMinimum(-1); + marginSlider->setValue(-1); + marginSlider->setMaximum(100); + // Add controls to the layout #ifdef MULTI_SERIES vLayout->addWidget(series1CB); @@ -491,7 +496,9 @@ int main(int argc, char *argv[]) vLayout2->addWidget(new QLabel(QStringLiteral("Camera target"))); vLayout2->addWidget(cameraTargetSliderX); vLayout2->addWidget(cameraTargetSliderY); - vLayout2->addWidget(cameraTargetSliderZ, 1, Qt::AlignTop); + vLayout2->addWidget(cameraTargetSliderZ); + vLayout2->addWidget(new QLabel(QStringLiteral("Adjust margin")), 0, Qt::AlignTop); + vLayout2->addWidget(marginSlider, 1, Qt::AlignTop); vLayout3->addWidget(labelButton); vLayout3->addWidget(meshButton); @@ -707,6 +714,8 @@ int main(int argc, char *argv[]) &GraphModifier::setCameraTargetY); QObject::connect(cameraTargetSliderZ, &QSlider::valueChanged, modifier, &GraphModifier::setCameraTargetZ); + QObject::connect(marginSlider, &QSlider::valueChanged, modifier, + &GraphModifier::setGraphMargin); #ifdef MULTI_SERIES modifier->setSeries1CB(series1CB); -- cgit v1.2.3 From 0c5d66e630c903c8a7fb63f56a19188a53b2c383 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Tue, 30 Sep 2014 15:25:16 +0300 Subject: Fix selection color and gradient for static optimization MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added separate shaders for rendering selected items, as the texture mapping that the regular static optimization shaders expect is not correct for single objects. Task-number: QTRD-3306 Change-Id: I6eb6cef94a4d2b4e5bdd03748f18db641a9fc4f6 Reviewed-by: Tomi Korpipää --- .../engine/abstract3drenderer.cpp | 24 +++++ .../engine/abstract3drenderer_p.h | 4 + src/datavisualization/engine/scatter3drenderer.cpp | 102 ++++++++++++++++----- src/datavisualization/engine/scatter3drenderer_p.h | 6 ++ 4 files changed, 111 insertions(+), 25 deletions(-) diff --git a/src/datavisualization/engine/abstract3drenderer.cpp b/src/datavisualization/engine/abstract3drenderer.cpp index aaf5418f..277ef03e 100644 --- a/src/datavisualization/engine/abstract3drenderer.cpp +++ b/src/datavisualization/engine/abstract3drenderer.cpp @@ -223,6 +223,18 @@ void Abstract3DRenderer::initGradientShaders(const QString &vertexShader, Q_UNUSED(fragmentShader) } +void Abstract3DRenderer::initStaticSelectedItemShaders(const QString &vertexShader, + const QString &fragmentShader, + const QString &gradientVertexShader, + const QString &gradientFragmentShader) +{ + // Do nothing by default + Q_UNUSED(vertexShader) + Q_UNUSED(fragmentShader) + Q_UNUSED(gradientVertexShader) + Q_UNUSED(gradientFragmentShader) +} + void Abstract3DRenderer::initCustomItemShaders(const QString &vertexShader, const QString &fragmentShader) { @@ -362,6 +374,10 @@ void Abstract3DRenderer::reInitShaders() && qobject_cast(this)) { initGradientShaders(QStringLiteral(":/shaders/vertexShadow"), QStringLiteral(":/shaders/fragmentShadow")); + initStaticSelectedItemShaders(QStringLiteral(":/shaders/vertexShadow"), + QStringLiteral(":/shaders/fragmentShadowNoTex"), + QStringLiteral(":/shaders/vertexShadow"), + QStringLiteral(":/shaders/fragmentShadowNoTexColorOnY")); initShaders(QStringLiteral(":/shaders/vertexShadowNoMatrices"), QStringLiteral(":/shaders/fragmentShadowNoTex")); } else { @@ -379,6 +395,10 @@ void Abstract3DRenderer::reInitShaders() && qobject_cast(this)) { initGradientShaders(QStringLiteral(":/shaders/vertexTexture"), QStringLiteral(":/shaders/fragmentTexture")); + initStaticSelectedItemShaders(QStringLiteral(":/shaders/vertex"), + QStringLiteral(":/shaders/fragment"), + QStringLiteral(":/shaders/vertex"), + QStringLiteral(":/shaders/fragmentColorOnY")); initShaders(QStringLiteral(":/shaders/vertexNoMatrices"), QStringLiteral(":/shaders/fragment")); } else { @@ -403,6 +423,10 @@ void Abstract3DRenderer::reInitShaders() && qobject_cast(this)) { initGradientShaders(QStringLiteral(":/shaders/vertexTexture"), QStringLiteral(":/shaders/fragmentTextureES2")); + initStaticSelectedItemShaders(QStringLiteral(":/shaders/vertex"), + QStringLiteral(":/shaders/fragmentES2"), + QStringLiteral(":/shaders/vertex"), + QStringLiteral(":/shaders/fragmentColorOnYES2")); initBackgroundShaders(QStringLiteral(":/shaders/vertexNoMatrices"), QStringLiteral(":/shaders/fragmentES2")); } else { diff --git a/src/datavisualization/engine/abstract3drenderer_p.h b/src/datavisualization/engine/abstract3drenderer_p.h index 997362d5..4833afaa 100644 --- a/src/datavisualization/engine/abstract3drenderer_p.h +++ b/src/datavisualization/engine/abstract3drenderer_p.h @@ -87,6 +87,10 @@ public: virtual void updateShadowQuality(QAbstract3DGraph::ShadowQuality quality) = 0; virtual void initShaders(const QString &vertexShader, const QString &fragmentShader) = 0; virtual void initGradientShaders(const QString &vertexShader, const QString &fragmentShader); + virtual void initStaticSelectedItemShaders(const QString &vertexShader, + const QString &fragmentShader, + const QString &gradientVertexShader, + const QString &gradientFragmentShader); virtual void initBackgroundShaders(const QString &vertexShader, const QString &fragmentShader) = 0; virtual void initCustomItemShaders(const QString &vertexShader, diff --git a/src/datavisualization/engine/scatter3drenderer.cpp b/src/datavisualization/engine/scatter3drenderer.cpp index 3bae9879..5fe69472 100644 --- a/src/datavisualization/engine/scatter3drenderer.cpp +++ b/src/datavisualization/engine/scatter3drenderer.cpp @@ -43,6 +43,8 @@ Scatter3DRenderer::Scatter3DRenderer(Scatter3DController *controller) m_updateLabels(false), m_dotShader(0), m_dotGradientShader(0), + m_staticSelectedItemGradientShader(0), + m_staticSelectedItemShader(0), #if defined(QT_OPENGL_ES_2) m_pointShader(0), #endif @@ -85,6 +87,8 @@ Scatter3DRenderer::~Scatter3DRenderer() m_textureHelper->deleteTexture(&m_bgrTexture); } delete m_dotShader; + delete m_staticSelectedItemGradientShader; + delete m_staticSelectedItemShader; delete m_dotGradientShader; delete m_depthShader; delete m_selectionShader; @@ -1007,6 +1011,17 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) && m_selectedItemIndex != Scatter3DController::invalidSelectionIndex()) { ScatterRenderItem &item = renderArray[m_selectedItemIndex]; if (item.isVisible()) { + ShaderHelper *selectionShader; + if (drawingPoints) { + selectionShader = pointSelectionShader; + } else { + if (colorStyleIsUniform) + selectionShader = m_staticSelectedItemShader; + else + selectionShader = m_staticSelectedItemGradientShader; + } + selectionShader->bind(); + ObjectHelper *dotObj = cache->object(); QMatrix4x4 modelMatrix; @@ -1021,6 +1036,15 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) } modelMatrix.scale(modelScaler); itModelMatrix.scale(modelScaler); + + selectionShader->setUniformValue(selectionShader->lightP(), + lightPos); + selectionShader->setUniformValue(selectionShader->view(), + viewMatrix); + selectionShader->setUniformValue(selectionShader->ambientS(), + m_cachedTheme->ambientLightStrength()); + selectionShader->setUniformValue(selectionShader->lightColor(), + lightColor); } QMatrix4x4 MVPMatrix; @@ -1044,19 +1068,29 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) if (!drawingPoints) { // Set shader bindings - dotShader->setUniformValue(dotShader->model(), modelMatrix); - dotShader->setUniformValue(dotShader->nModel(), - itModelMatrix.inverted().transposed()); + selectionShader->setUniformValue(selectionShader->model(), modelMatrix); + selectionShader->setUniformValue(selectionShader->nModel(), + itModelMatrix.inverted().transposed()); + if (!colorStyleIsUniform) { + if (colorStyle == Q3DTheme::ColorStyleObjectGradient) { + selectionShader->setUniformValue(selectionShader->gradientMin(), + 0.0f); + selectionShader->setUniformValue(selectionShader->gradientHeight(), + 0.5f); + } else { + // Each dot is of uniform color according to its Y-coordinate + selectionShader->setUniformValue(selectionShader->gradientHeight(), + 0.0f); + selectionShader->setUniformValue(selectionShader->gradientMin(), + (item.translation().y() + m_scaleY) + * rangeGradientYScaler); + } + } } - dotShader->setUniformValue(dotShader->MVP(), MVPMatrix); - if (useColor) { - dotShader->setUniformValue(dotShader->color(), dotColor); - } else if (colorStyle == Q3DTheme::ColorStyleRangeGradient) { - dotShader->setUniformValue(dotShader->gradientMin(), - (item.translation().y() + m_scaleY) - * rangeGradientYScaler); - } + selectionShader->setUniformValue(selectionShader->MVP(), MVPMatrix); + if (useColor) + selectionShader->setUniformValue(selectionShader->color(), dotColor); if (!drawingPoints) { glEnable(GL_POLYGON_OFFSET_FILL); @@ -1068,26 +1102,32 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) if (!drawingPoints) { // Set shadow shader bindings QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix; - dotShader->setUniformValue(dotShader->depth(), depthMVPMatrix); - dotShader->setUniformValue(dotShader->lightS(), lightStrength / 10.0f); + selectionShader->setUniformValue(selectionShader->shadowQ(), + m_shadowQualityToShader); + selectionShader->setUniformValue(selectionShader->depth(), + depthMVPMatrix); + selectionShader->setUniformValue(selectionShader->lightS(), + lightStrength / 10.0f); // Draw the object - m_drawer->drawObject(dotShader, dotObj, gradientTexture, m_depthTexture); + m_drawer->drawObject(selectionShader, dotObj, gradientTexture, + m_depthTexture); } else { // Draw the object - m_drawer->drawPoint(dotShader); + m_drawer->drawPoint(selectionShader); } } else #endif { if (!drawingPoints) { // Set shadowless shader bindings - dotShader->setUniformValue(dotShader->lightS(), lightStrength); + selectionShader->setUniformValue(selectionShader->lightS(), + lightStrength); // Draw the object - m_drawer->drawObject(dotShader, dotObj, gradientTexture); + m_drawer->drawObject(selectionShader, dotObj, gradientTexture); } else { // Draw the object - m_drawer->drawPoint(dotShader); + m_drawer->drawPoint(selectionShader); } } @@ -2235,8 +2275,7 @@ void Scatter3DRenderer::calculateSceneScalingFactors() void Scatter3DRenderer::initShaders(const QString &vertexShader, const QString &fragmentShader) { - if (m_dotShader) - delete m_dotShader; + delete m_dotShader; m_dotShader = new ShaderHelper(this, vertexShader, fragmentShader); m_dotShader->initialize(); } @@ -2244,17 +2283,30 @@ void Scatter3DRenderer::initShaders(const QString &vertexShader, const QString & void Scatter3DRenderer::initGradientShaders(const QString &vertexShader, const QString &fragmentShader) { - if (m_dotGradientShader) - delete m_dotGradientShader; - + delete m_dotGradientShader; m_dotGradientShader = new ShaderHelper(this, vertexShader, fragmentShader); m_dotGradientShader->initialize(); + +} + +void Scatter3DRenderer::initStaticSelectedItemShaders(const QString &vertexShader, + const QString &fragmentShader, + const QString &gradientVertexShader, + const QString &gradientFragmentShader) +{ + delete m_staticSelectedItemShader; + m_staticSelectedItemShader = new ShaderHelper(this, vertexShader, fragmentShader); + m_staticSelectedItemShader->initialize(); + + delete m_staticSelectedItemGradientShader; + m_staticSelectedItemGradientShader = new ShaderHelper(this, gradientVertexShader, + gradientFragmentShader); + m_staticSelectedItemGradientShader->initialize(); } void Scatter3DRenderer::initSelectionShader() { - if (m_selectionShader) - delete m_selectionShader; + delete m_selectionShader; m_selectionShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexPlainColor"), QStringLiteral(":/shaders/fragmentPlainColor")); m_selectionShader->initialize(); diff --git a/src/datavisualization/engine/scatter3drenderer_p.h b/src/datavisualization/engine/scatter3drenderer_p.h index d882d59f..f492fc05 100644 --- a/src/datavisualization/engine/scatter3drenderer_p.h +++ b/src/datavisualization/engine/scatter3drenderer_p.h @@ -53,6 +53,8 @@ private: bool m_updateLabels; ShaderHelper *m_dotShader; ShaderHelper *m_dotGradientShader; + ShaderHelper *m_staticSelectedItemGradientShader; + ShaderHelper *m_staticSelectedItemShader; #if defined(QT_OPENGL_ES_2) ShaderHelper *m_pointShader; #endif @@ -116,6 +118,10 @@ protected: private: virtual void initShaders(const QString &vertexShader, const QString &fragmentShader); virtual void initGradientShaders(const QString &vertexShader, const QString &fragmentShader); + virtual void initStaticSelectedItemShaders(const QString &vertexShader, + const QString &fragmentShader, + const QString &gradientVertexShader, + const QString &gradientFragmentShader); virtual void updateShadowQuality(QAbstract3DGraph::ShadowQuality quality); virtual void updateTextures(); virtual void fixMeshFileName(QString &fileName, QAbstract3DSeries::Mesh mesh); -- cgit v1.2.3 From 7d79c15989cf75da3d3d3591d6b139b91b3e0d43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomi=20Korpip=C3=A4=C3=A4?= Date: Wed, 1 Oct 2014 10:48:15 +0300 Subject: Reduce font size if label gets too wide to fit texture Task-number: QTRD-3230 Change-Id: I0cb76f077c1930033c03f833c9c9f2d347c41f9c Reviewed-by: Mika Salmela Reviewed-by: Miikka Heikkinen --- src/datavisualization/utils/utils.cpp | 75 ++++++++++++++++++++++++++--------- 1 file changed, 56 insertions(+), 19 deletions(-) diff --git a/src/datavisualization/utils/utils.cpp b/src/datavisualization/utils/utils.cpp index b2bae478..d588ee16 100644 --- a/src/datavisualization/utils/utils.cpp +++ b/src/datavisualization/utils/utils.cpp @@ -25,6 +25,8 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION #define NUM_IN_POWER(y, x) for (;y Date: Thu, 2 Oct 2014 10:28:42 +0300 Subject: Fixed ordering of subviews Task-number: QTRD-2790 Change-Id: I8ac6ce89920a9c988c7a059e1b02c980a0264200 Reviewed-by: Miikka Heikkinen --- src/datavisualization/engine/q3dscene.cpp | 40 +++++++++++++++++++++---------- src/datavisualization/engine/q3dscene_p.h | 2 ++ tests/barstest/chart.cpp | 4 +++- 3 files changed, 32 insertions(+), 14 deletions(-) diff --git a/src/datavisualization/engine/q3dscene.cpp b/src/datavisualization/engine/q3dscene.cpp index 87826a8b..6ea41d6a 100644 --- a/src/datavisualization/engine/q3dscene.cpp +++ b/src/datavisualization/engine/q3dscene.cpp @@ -159,7 +159,6 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION * A constant property providing an invalid point for selection. */ - /*! * Constructs a basic scene with one light and one camera in it. An * optional \a parent parameter can be given and is then passed to QObject constructor. @@ -219,33 +218,35 @@ void Q3DScene::setPrimarySubViewport(const QRect &primarySubViewport) /*! * Returns whether the given \a point resides inside the primary subview or not. * \return \c true if the point is inside the primary subview. + * \note If subviews are superimposed, and the given \a point resides inside both, result is + * \c true only when the primary subview is on top. */ bool Q3DScene::isPointInPrimarySubView(const QPoint &point) { int x = point.x(); int y = point.y(); - int areaMinX = d_ptr->m_primarySubViewport.x(); - int areaMaxX = d_ptr->m_primarySubViewport.x() + d_ptr->m_primarySubViewport.width(); - int areaMinY = d_ptr->m_primarySubViewport.y(); - int areaMaxY = d_ptr->m_primarySubViewport.y() + d_ptr->m_primarySubViewport.height(); - - return ( x > areaMinX && x < areaMaxX && y > areaMinY && y < areaMaxY ); + bool isInSecondary = d_ptr->isInArea(d_ptr->m_secondarySubViewport, x, y); + if (!isInSecondary || (isInSecondary && !d_ptr->m_isSecondarySubviewOnTop)) + return d_ptr->isInArea(d_ptr->m_primarySubViewport, x, y); + else + return false; } /*! * Returns whether the given \a point resides inside the secondary subview or not. * \return \c true if the point is inside the secondary subview. + * \note If subviews are superimposed, and the given \a point resides inside both, result is + * \c true only when the secondary subview is on top. */ bool Q3DScene::isPointInSecondarySubView(const QPoint &point) { int x = point.x(); int y = point.y(); - int areaMinX = d_ptr->m_secondarySubViewport.x(); - int areaMaxX = d_ptr->m_secondarySubViewport.x() + d_ptr->m_secondarySubViewport.width(); - int areaMinY = d_ptr->m_secondarySubViewport.y(); - int areaMaxY = d_ptr->m_secondarySubViewport.y() + d_ptr->m_secondarySubViewport.height(); - - return ( x > areaMinX && x < areaMaxX && y > areaMinY && y < areaMaxY ); + bool isInPrimary = d_ptr->isInArea(d_ptr->m_primarySubViewport, x, y); + if (!isInPrimary || (isInPrimary && d_ptr->m_isSecondarySubviewOnTop)) + return d_ptr->isInArea(d_ptr->m_secondarySubViewport, x, y); + else + return false; } /*! @@ -368,6 +369,10 @@ void Q3DScene::setSlicingActive(bool isSlicing) d_ptr->m_changeTracker.slicingActivatedChanged = true; d_ptr->m_sceneDirty = true; + // Set secondary subview behind primary to achieve default functionality (= clicking on + // primary disables slice) + setSecondarySubviewOnTop(!isSlicing); + d_ptr->calculateSubViewports(); emit slicingActiveChanged(isSlicing); emit d_ptr->needRender(); @@ -719,4 +724,13 @@ void Q3DScenePrivate::markDirty() emit needRender(); } +bool Q3DScenePrivate::isInArea(const QRect &area, int x, int y) const +{ + int areaMinX = area.x(); + int areaMaxX = area.x() + area.width(); + int areaMinY = area.y(); + int areaMaxY = area.y() + area.height(); + return ( x >= areaMinX && x <= areaMaxX && y >= areaMinY && y <= areaMaxY ); +} + QT_END_NAMESPACE_DATAVISUALIZATION diff --git a/src/datavisualization/engine/q3dscene_p.h b/src/datavisualization/engine/q3dscene_p.h index b7321c25..d0f6e735 100644 --- a/src/datavisualization/engine/q3dscene_p.h +++ b/src/datavisualization/engine/q3dscene_p.h @@ -93,6 +93,8 @@ public: void markDirty(); + bool isInArea(const QRect &area, int x, int y) const; + signals: void needRender(); diff --git a/tests/barstest/chart.cpp b/tests/barstest/chart.cpp index 6be5e66d..e468700e 100644 --- a/tests/barstest/chart.cpp +++ b/tests/barstest/chart.cpp @@ -203,7 +203,6 @@ GraphModifier::GraphModifier(Q3DBars *barchart, QColorDialog *colorDialog) m_graph->activeTheme()->setFont(QFont("Times Roman", 20)); - // Release and store the default input handler. m_defaultInputHandler = static_cast(m_graph->activeInputHandler()); m_graph->releaseInputHandler(m_defaultInputHandler); @@ -335,6 +334,9 @@ void GraphModifier::releaseSeries() void GraphModifier::flipViews() { m_graph->scene()->setSecondarySubviewOnTop(!m_graph->scene()->isSecondarySubviewOnTop()); + qDebug() << "secondary subview on top:" << m_graph->scene()->isSecondarySubviewOnTop(); + qDebug() << "point (50, 50) in primary subview:" << m_graph->scene()->isPointInPrimarySubView(QPoint(50, 50)); + qDebug() << "point (50, 50) in secondary subview:" << m_graph->scene()->isPointInSecondarySubView(QPoint(50, 50)); } void GraphModifier::createMassiveArray() -- cgit v1.2.3 From b1dd198ff8fee3e1cfe09ed37d0fdea55c37b7d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomi=20Korpip=C3=A4=C3=A4?= Date: Thu, 2 Oct 2014 10:33:30 +0300 Subject: Fixed window blend bug Alpha buffer size should not be set for the window's surface format. Task-number: QTRD-3351 Change-Id: I64ee61990e8ceb88bdbab715a2902b454619f3e5 Reviewed-by: Miikka Heikkinen --- src/datavisualization/utils/qutils.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/datavisualization/utils/qutils.h b/src/datavisualization/utils/qutils.h index 92bd632c..1e0eb9f9 100644 --- a/src/datavisualization/utils/qutils.h +++ b/src/datavisualization/utils/qutils.h @@ -36,7 +36,6 @@ inline static QSurfaceFormat qDefaultSurfaceFormat(bool antialias = true) // Antialias not supported for ES antialias = false; surfaceFormat.setRenderableType(QSurfaceFormat::OpenGLES); - surfaceFormat.setAlphaBufferSize(8); surfaceFormat.setRedBufferSize(8); surfaceFormat.setBlueBufferSize(8); surfaceFormat.setGreenBufferSize(8); -- cgit v1.2.3 From df2571bdf0b15237a324471c63913989c80349d8 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Thu, 2 Oct 2014 16:19:33 +0300 Subject: Fix changing items in static optimization mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Task-number: QTRD-3344 Change-Id: I8c1d2e2ae4afd6f9058c59adfc3d784b5f724358 Reviewed-by: Tomi Korpipää --- .../engine/abstract3drenderer.cpp | 2 + src/datavisualization/engine/scatter3drenderer.cpp | 87 ++++++++++++++++----- .../engine/scatterseriesrendercache.cpp | 3 +- .../engine/scatterseriesrendercache_p.h | 7 ++ .../utils/scatterobjectbufferhelper.cpp | 89 +++++++++++++-------- .../utils/scatterpointbufferhelper.cpp | 91 +++++++++++++++------- .../utils/scatterpointbufferhelper_p.h | 4 +- 7 files changed, 202 insertions(+), 81 deletions(-) diff --git a/src/datavisualization/engine/abstract3drenderer.cpp b/src/datavisualization/engine/abstract3drenderer.cpp index 277ef03e..0cbf2e26 100644 --- a/src/datavisualization/engine/abstract3drenderer.cpp +++ b/src/datavisualization/engine/abstract3drenderer.cpp @@ -495,6 +495,8 @@ void Abstract3DRenderer::updateMargin(float margin) void Abstract3DRenderer::updateOptimizationHint(QAbstract3DGraph::OptimizationHints hint) { m_cachedOptimizationHint = hint; + foreach (SeriesRenderCache *cache, m_renderCacheList) + cache->setDataDirty(true); } void Abstract3DRenderer::handleResize() diff --git a/src/datavisualization/engine/scatter3drenderer.cpp b/src/datavisualization/engine/scatter3drenderer.cpp index 5fe69472..b2e0e6e4 100644 --- a/src/datavisualization/engine/scatter3drenderer.cpp +++ b/src/datavisualization/engine/scatter3drenderer.cpp @@ -260,11 +260,13 @@ void Scatter3DRenderer::updateSeries(const QList &seriesLis QScatter3DSeries *scatterSeries = static_cast(seriesList[i]); if (scatterSeries->isVisible()) { QAbstract3DSeriesChangeBitField &changeTracker = scatterSeries->d_ptr->m_changeTracker; - if (changeTracker.baseGradientChanged || changeTracker.colorStyleChanged) { - ScatterSeriesRenderCache *cache = - static_cast(m_renderCacheList.value(scatterSeries)); - if (cache) + ScatterSeriesRenderCache *cache = + static_cast(m_renderCacheList.value(scatterSeries)); + if (cache) { + if (changeTracker.baseGradientChanged || changeTracker.colorStyleChanged) cache->setStaticObjectUVDirty(true); + if (cache->itemSize() != scatterSeries->itemSize()) + cache->setStaticBufferDirty(true); } } } @@ -308,6 +310,13 @@ void Scatter3DRenderer::updateSeries(const QList &seriesLis m_haveGradientMeshSeries = true; } + if (cache->staticBufferDirty()) { + if (cache->mesh() != QAbstract3DSeries::MeshPoint) { + ScatterObjectBufferHelper *object = cache->bufferObject(); + object->update(cache, m_dotSizeScale); + } + cache->setStaticBufferDirty(false); + } if (cache->staticObjectUVDirty()) { if (cache->mesh() == QAbstract3DSeries::MeshPoint) { ScatterPointBufferHelper *object = cache->bufferPoints(); @@ -340,6 +349,8 @@ void Scatter3DRenderer::updateItems(const QVectorisVisible()) { const int index = item.index; - updateRenderItem(dataArray->at(index), cache->renderArray()[index]); + bool oldVisibility; + ScatterRenderItem &item = cache->renderArray()[index]; + if (optimizationStatic) + oldVisibility = item.isVisible(); + updateRenderItem(dataArray->at(index), item); + if (optimizationStatic) { + if (!cache->visibilityChanged() && oldVisibility != item.isVisible()) + cache->setVisibilityChanged(true); + cache->updateIndices().append(index); + } + } + } + if (optimizationStatic) { + foreach (SeriesRenderCache *baseCache, m_renderCacheList) { + ScatterSeriesRenderCache *cache = static_cast(baseCache); + if (cache->isVisible() && cache->updateIndices().size()) { + if (cache->mesh() == QAbstract3DSeries::MeshPoint) { + cache->bufferPoints()->update(cache); + if (cache->colorStyle() == Q3DTheme::ColorStyleRangeGradient) + cache->bufferPoints()->updateUVs(cache); + } else { + if (cache->visibilityChanged()) { + // If any change changes item visibility, full load is needed to + // resize the buffers. + cache->updateIndices().clear(); + cache->bufferObject()->fullLoad(cache, m_dotSizeScale); + } else { + cache->bufferObject()->update(cache, m_dotSizeScale); + if (cache->colorStyle() == Q3DTheme::ColorStyleRangeGradient) + cache->bufferObject()->updateUVs(cache); + } + } + cache->updateIndices().clear(); + } + cache->setVisibilityChanged(false); } } } @@ -801,8 +846,6 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) } } else { dotShader = pointSelectionShader; - previousDrawingPoints = true; - dotShader->bind(); } float rangeGradientYScaler = 0.5f / m_scaleY; @@ -847,7 +890,15 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) == Q3DTheme::ColorStyleUniform)))) { previousDrawingPoints = drawingPoints; if (drawingPoints) { - dotShader = pointSelectionShader; + if (!optimizationDefault && rangeGradientPoints) { +#if !defined(QT_OPENGL_ES_2) + dotShader = m_labelShader; +#else + dotShader = m_staticGradientPointShader; +#endif + } else { + dotShader = pointSelectionShader; + } } else { if (colorStyleIsUniform) dotShader = m_dotShader; @@ -859,13 +910,13 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) if (!drawingPoints && !colorStyleIsUniform && previousMeshColorStyle != colorStyle) { if (colorStyle == Q3DTheme::ColorStyleObjectGradient) { - m_dotGradientShader->setUniformValue(m_dotGradientShader->gradientMin(), 0.0f); - m_dotGradientShader->setUniformValue(m_dotGradientShader->gradientHeight(), - 0.5f); + dotShader->setUniformValue(dotShader->gradientMin(), 0.0f); + dotShader->setUniformValue(dotShader->gradientHeight(), + 0.5f); } else { // Each dot is of uniform color according to its Y-coordinate - m_dotGradientShader->setUniformValue(m_dotGradientShader->gradientHeight(), - 0.0f); + dotShader->setUniformValue(dotShader->gradientHeight(), + 0.0f); } } @@ -921,15 +972,8 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) gradientTexture = cache->baseGradientTexture(); } - if (!optimizationDefault && rangeGradientPoints) { -#if !defined(QT_OPENGL_ES_2) - dotShader = m_labelShader; -#else - dotShader = m_staticGradientPointShader; -#endif - dotShader->bind(); + if (!optimizationDefault && rangeGradientPoints) gradientTexture = cache->baseGradientTexture(); - } GLfloat lightStrength = m_cachedTheme->lightStrength(); if (optimizationDefault && selectedSeries && (m_selectedItemIndex == i)) { @@ -1134,6 +1178,7 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) if (!drawingPoints) glDisable(GL_POLYGON_OFFSET_FILL); } + dotShader->bind(); } } } diff --git a/src/datavisualization/engine/scatterseriesrendercache.cpp b/src/datavisualization/engine/scatterseriesrendercache.cpp index 4bc733ac..4930dde1 100644 --- a/src/datavisualization/engine/scatterseriesrendercache.cpp +++ b/src/datavisualization/engine/scatterseriesrendercache.cpp @@ -31,7 +31,8 @@ ScatterSeriesRenderCache::ScatterSeriesRenderCache(QAbstract3DSeries *series, m_oldRenderArraySize(0), m_oldMeshFileName(QString()), m_scatterBufferObj(0), - m_scatterBufferPoints(0) + m_scatterBufferPoints(0), + m_visibilityChanged(false) { } diff --git a/src/datavisualization/engine/scatterseriesrendercache_p.h b/src/datavisualization/engine/scatterseriesrendercache_p.h index e7643748..9c6e8e8f 100644 --- a/src/datavisualization/engine/scatterseriesrendercache_p.h +++ b/src/datavisualization/engine/scatterseriesrendercache_p.h @@ -63,6 +63,10 @@ public: inline ScatterObjectBufferHelper *bufferObject() const { return m_scatterBufferObj; } inline void setBufferPoints(ScatterPointBufferHelper *object) { m_scatterBufferPoints = object; } inline ScatterPointBufferHelper *bufferPoints() const { return m_scatterBufferPoints; } + inline QVector &updateIndices() { return m_updateIndices; } + inline QVector &bufferIndices() { return m_bufferIndices; } + inline void setVisibilityChanged(bool changed) { m_visibilityChanged = changed; } + inline bool visibilityChanged() const { return m_visibilityChanged; } protected: ScatterRenderItemArray m_renderArray; @@ -73,6 +77,9 @@ protected: QString m_oldMeshFileName; // Used to detect if full buffer change needed ScatterObjectBufferHelper *m_scatterBufferObj; ScatterPointBufferHelper *m_scatterBufferPoints; + QVector m_updateIndices; // Used as temporary cache during item updates + QVector m_bufferIndices; // Cache for mapping renderarray to mesh buffer + bool m_visibilityChanged; // Used to detect if full buffer change needed }; QT_END_NAMESPACE_DATAVISUALIZATION diff --git a/src/datavisualization/utils/scatterobjectbufferhelper.cpp b/src/datavisualization/utils/scatterobjectbufferhelper.cpp index 5e98363e..5eb74011 100644 --- a/src/datavisualization/utils/scatterobjectbufferhelper.cpp +++ b/src/datavisualization/utils/scatterobjectbufferhelper.cpp @@ -49,7 +49,7 @@ void ScatterObjectBufferHelper::fullLoad(ScatterSeriesRenderCache *cache, qreal if (renderArraySize == 0) return; // No use to go forward - uint itemCount = renderArraySize; + uint itemCount = 0; QQuaternion seriesRotation(cache->meshRotation()); if (m_meshDataLoaded) { @@ -96,7 +96,6 @@ void ScatterObjectBufferHelper::fullLoad(ScatterSeriesRenderCache *cache, qreal buffered_vertices.resize(verticeCount * renderArraySize); buffered_normals.resize(normalsCount * renderArraySize); buffered_uvs.resize(uvsCount * renderArraySize); - uint pos = 0; if (cache->colorStyle() == Q3DTheme::ColorStyleRangeGradient) createRangeGradientUVs(cache, buffered_uvs); @@ -105,14 +104,16 @@ void ScatterObjectBufferHelper::fullLoad(ScatterSeriesRenderCache *cache, qreal QVector2D dummyUV(0.0f, 0.0f); + cache->bufferIndices().resize(renderArraySize); + for (uint i = 0; i < renderArraySize; i++) { const ScatterRenderItem &item = renderArray.at(i); - if (!item.isVisible()) { - itemCount--; + if (!item.isVisible()) continue; - } + else + cache->bufferIndices()[i] = itemCount; - int offset = pos * verticeCount; + int offset = itemCount * verticeCount; if (item.rotation().isIdentity()) { for (int j = 0; j < verticeCount; j++) { buffered_vertices[j + offset] = scaled_vertices[j] + item.translation(); @@ -134,18 +135,17 @@ void ScatterObjectBufferHelper::fullLoad(ScatterSeriesRenderCache *cache, qreal } if (cache->colorStyle() == Q3DTheme::ColorStyleUniform) { - offset = pos * uvsCount; + offset = itemCount * uvsCount; for (int j = 0; j < uvsCount; j++) buffered_uvs[j + offset] = dummyUV; } - int offsetVertice = i * verticeCount; - offset = pos * indicesCount; - for (int j = 0; j < indicesCount; j++) { + int offsetVertice = itemCount * verticeCount; + offset = itemCount * indicesCount; + for (int j = 0; j < indicesCount; j++) buffered_indices[j + offset] = GLuint(indices[j] + offsetVertice); - } - pos++; + itemCount++; } m_indexCount = indicesCount * itemCount; @@ -185,9 +185,14 @@ void ScatterObjectBufferHelper::updateUVs(ScatterSeriesRenderCache *cache) ObjectHelper *dotObj = cache->object(); const int uvsCount = dotObj->indexedUVs().count(); const ScatterRenderItemArray &renderArray = cache->renderArray(); - const uint renderArraySize = renderArray.size(); + const bool updateAll = (cache->updateIndices().size() == 0); + const int updateSize = updateAll ? renderArray.size() : cache->updateIndices().size(); + + if (!updateSize) + return; + QVector buffered_uvs; - buffered_uvs.resize(uvsCount * renderArraySize); + buffered_uvs.resize(uvsCount * updateSize); uint itemCount = 0; if (cache->colorStyle() == Q3DTheme::ColorStyleRangeGradient) { @@ -198,9 +203,20 @@ void ScatterObjectBufferHelper::updateUVs(ScatterSeriesRenderCache *cache) } glBindBuffer(GL_ARRAY_BUFFER, m_uvbuffer); - glBufferData(GL_ARRAY_BUFFER, uvsCount * itemCount * sizeof(QVector2D), - &buffered_uvs.at(0), GL_STATIC_DRAW); - + int itemSize = uvsCount * sizeof(QVector2D); + if (cache->updateIndices().size()) { + int pos = 0; + for (int i = 0; i < updateSize; i++) { + int index = cache->updateIndices().at(i); + if (renderArray.at(index).isVisible()) { + int dataPos = cache->bufferIndices().at(index); + glBufferSubData(GL_ARRAY_BUFFER, itemSize * dataPos, itemSize, + &buffered_uvs.at(uvsCount * pos++)); + } + } + } else { + glBufferData(GL_ARRAY_BUFFER, itemSize * itemCount, &buffered_uvs.at(0), GL_STATIC_DRAW); + } glBindBuffer(GL_ARRAY_BUFFER, 0); } @@ -210,13 +226,15 @@ uint ScatterObjectBufferHelper::createRangeGradientUVs(ScatterSeriesRenderCache ObjectHelper *dotObj = cache->object(); const int uvsCount = dotObj->indexedUVs().count(); const ScatterRenderItemArray &renderArray = cache->renderArray(); - const uint renderArraySize = renderArray.size(); + const bool updateAll = (cache->updateIndices().size() == 0); + const int updateSize = updateAll ? renderArray.size() : cache->updateIndices().size(); QVector2D uv; uv.setX(0.0f); uint pos = 0; - for (uint i = 0; i < renderArraySize; i++) { - const ScatterRenderItem &item = renderArray.at(i); + for (int i = 0; i < updateSize; i++) { + int index = updateAll ? i : cache->updateIndices().at(i); + const ScatterRenderItem &item = renderArray.at(index); if (!item.isVisible()) continue; @@ -263,13 +281,15 @@ uint ScatterObjectBufferHelper::createObjectGradientUVs(ScatterSeriesRenderCache void ScatterObjectBufferHelper::update(ScatterSeriesRenderCache *cache, qreal dotScale) { - initializeOpenGLFunctions(); - ObjectHelper *dotObj = cache->object(); const ScatterRenderItemArray &renderArray = cache->renderArray(); - const int renderArraySize = renderArray.size(); + const bool updateAll = (cache->updateIndices().size() == 0); + const int updateSize = updateAll ? renderArray.size() : cache->updateIndices().size(); QQuaternion seriesRotation(cache->meshRotation()); + if (!updateSize) + return; + // Index vertices const QVector indexed_vertices = dotObj->indexedvertices(); int verticeCount = indexed_vertices.count(); @@ -292,10 +312,11 @@ void ScatterObjectBufferHelper::update(ScatterSeriesRenderCache *cache, qreal do scaled_vertices[i] = indexed_vertices[i] * modelMatrix; QVector buffered_vertices; + buffered_vertices.resize(verticeCount * updateSize); - buffered_vertices.resize(verticeCount * renderArraySize); - for (int i = 0; i < renderArraySize; i++) { - const ScatterRenderItem &item = renderArray.at(i); + for (int i = 0; i < updateSize; i++) { + int index = updateAll ? i : cache->updateIndices().at(i); + const ScatterRenderItem &item = renderArray.at(index); if (!item.isVisible()) continue; @@ -316,12 +337,18 @@ void ScatterObjectBufferHelper::update(ScatterSeriesRenderCache *cache, qreal do } glBindBuffer(GL_ARRAY_BUFFER, m_vertexbuffer); - glBufferData(GL_ARRAY_BUFFER, buffered_vertices.size() * sizeof(QVector3D), - &buffered_vertices.at(0), - GL_DYNAMIC_DRAW); - + if (updateAll) { + glBufferData(GL_ARRAY_BUFFER, buffered_vertices.size() * sizeof(QVector3D), + &buffered_vertices.at(0), GL_DYNAMIC_DRAW); + } else { + int itemSize = verticeCount * sizeof(QVector3D); + for (int i = 0; i < updateSize; i++) { + int index = updateAll ? i : cache->updateIndices().at(i); + glBufferSubData(GL_ARRAY_BUFFER, cache->bufferIndices().at(index) * itemSize, + itemSize, &buffered_vertices.at(i * verticeCount)); + } + } glBindBuffer(GL_ARRAY_BUFFER, 0); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); m_meshDataLoaded = true; } diff --git a/src/datavisualization/utils/scatterpointbufferhelper.cpp b/src/datavisualization/utils/scatterpointbufferhelper.cpp index 2ddee31c..5076502f 100644 --- a/src/datavisualization/utils/scatterpointbufferhelper.cpp +++ b/src/datavisualization/utils/scatterpointbufferhelper.cpp @@ -25,8 +25,7 @@ const QVector3D hiddenPos(-1000.0f, -1000.0f, -1000.0f); ScatterPointBufferHelper::ScatterPointBufferHelper() : m_pointbuffer(0), - m_oldRemoveIndex(0), - m_oldRemove(false) + m_oldRemoveIndex(-1) { m_indicesType = GL_UNSIGNED_INT; } @@ -48,7 +47,8 @@ void ScatterPointBufferHelper::pushPoint(uint pointIndex) { glBindBuffer(GL_ARRAY_BUFFER, m_pointbuffer); - if (m_oldRemove && m_oldRemoveIndex < pointIndex) { + // Pop the previous point if it is still pushed + if (m_oldRemoveIndex >= 0) { glBufferSubData(GL_ARRAY_BUFFER, m_oldRemoveIndex * sizeof(QVector3D), sizeof(QVector3D), &m_bufferedPoints.at(m_oldRemoveIndex)); } @@ -60,20 +60,18 @@ void ScatterPointBufferHelper::pushPoint(uint pointIndex) glBindBuffer(GL_ARRAY_BUFFER, 0); m_oldRemoveIndex = pointIndex; - m_oldRemove = true; } void ScatterPointBufferHelper::popPoint() { - if (m_oldRemove) { + if (m_oldRemoveIndex >= 0) { glBindBuffer(GL_ARRAY_BUFFER, m_pointbuffer); glBufferSubData(GL_ARRAY_BUFFER, m_oldRemoveIndex * sizeof(QVector3D), sizeof(QVector3D), &m_bufferedPoints.at(m_oldRemoveIndex)); glBindBuffer(GL_ARRAY_BUFFER, 0); } - m_oldRemoveIndex = 0; - m_oldRemove = false; + m_oldRemoveIndex = -1; } void ScatterPointBufferHelper::load(ScatterSeriesRenderCache *cache) @@ -91,14 +89,10 @@ void ScatterPointBufferHelper::load(ScatterSeriesRenderCache *cache) m_bufferedPoints.clear(); } - QVector buffered_uvs; - if (cache->colorStyle() == Q3DTheme::ColorStyleRangeGradient) - createRangeGradientUVs(cache, buffered_uvs); - bool itemsVisible = false; m_bufferedPoints.resize(renderArraySize); for (int i = 0; i < renderArraySize; i++) { - ScatterRenderItem &item = renderArray[i]; + const ScatterRenderItem &item = renderArray.at(i); if (!item.isVisible()) { m_bufferedPoints[i] = hiddenPos; } else { @@ -107,10 +101,14 @@ void ScatterPointBufferHelper::load(ScatterSeriesRenderCache *cache) } } + QVector buffered_uvs; if (itemsVisible) m_indexCount = renderArraySize; if (m_indexCount > 0) { + if (cache->colorStyle() == Q3DTheme::ColorStyleRangeGradient) + createRangeGradientUVs(cache, buffered_uvs); + glGenBuffers(1, &m_pointbuffer); glBindBuffer(GL_ARRAY_BUFFER, m_pointbuffer); glBufferData(GL_ARRAY_BUFFER, m_bufferedPoints.size() * sizeof(QVector3D), @@ -130,21 +128,60 @@ void ScatterPointBufferHelper::load(ScatterSeriesRenderCache *cache) } } -void ScatterPointBufferHelper::updateUVs(ScatterSeriesRenderCache *cache) +void ScatterPointBufferHelper::update(ScatterSeriesRenderCache *cache) { - QVector buffered_uvs; + // It may be that the buffer hasn't yet been initialized, in case the entire series was + // hidden items. No need to update in that case. + if (m_indexCount > 0) { + const ScatterRenderItemArray &renderArray = cache->renderArray(); + const int updateSize = cache->updateIndices().size(); - createRangeGradientUVs(cache, buffered_uvs); + glBindBuffer(GL_ARRAY_BUFFER, m_pointbuffer); + for (int i = 0; i < updateSize; i++) { + int index = cache->updateIndices().at(i); + const ScatterRenderItem &item = renderArray.at(index); + if (!item.isVisible()) + m_bufferedPoints[index] = hiddenPos; + else + m_bufferedPoints[index] = item.translation(); + + if (index != m_oldRemoveIndex) { + glBufferSubData(GL_ARRAY_BUFFER, index * sizeof(QVector3D), + sizeof(QVector3D), &m_bufferedPoints.at(index)); + } + } + glBindBuffer(GL_ARRAY_BUFFER, 0); + } +} - if (buffered_uvs.size()) { - if (!m_uvbuffer) - glGenBuffers(1, &m_uvbuffer); +void ScatterPointBufferHelper::updateUVs(ScatterSeriesRenderCache *cache) +{ + // It may be that the buffer hasn't yet been initialized, in case the entire series was + // hidden items. No need to update in that case. + if (m_indexCount > 0) { + QVector buffered_uvs; + createRangeGradientUVs(cache, buffered_uvs); - glBindBuffer(GL_ARRAY_BUFFER, m_uvbuffer); - glBufferData(GL_ARRAY_BUFFER, buffered_uvs.size() * sizeof(QVector2D), - &buffered_uvs.at(0), GL_STATIC_DRAW); + if (buffered_uvs.size()) { + if (!m_uvbuffer) + glGenBuffers(1, &m_uvbuffer); - glBindBuffer(GL_ARRAY_BUFFER, 0); + int updateSize = cache->updateIndices().size(); + glBindBuffer(GL_ARRAY_BUFFER, m_uvbuffer); + if (updateSize) { + for (int i = 0; i < updateSize; i++) { + int index = cache->updateIndices().at(i); + glBufferSubData(GL_ARRAY_BUFFER, index * sizeof(QVector2D), + sizeof(QVector2D), &buffered_uvs.at(i)); + + } + } else { + glBufferData(GL_ARRAY_BUFFER, buffered_uvs.size() * sizeof(QVector2D), + &buffered_uvs.at(0), GL_STATIC_DRAW); + } + + glBindBuffer(GL_ARRAY_BUFFER, 0); + } } } @@ -152,13 +189,15 @@ void ScatterPointBufferHelper::createRangeGradientUVs(ScatterSeriesRenderCache * QVector &buffered_uvs) { const ScatterRenderItemArray &renderArray = cache->renderArray(); - const uint renderArraySize = renderArray.size(); - buffered_uvs.resize(renderArraySize); + const bool updateAll = (cache->updateIndices().size() == 0); + const int updateSize = updateAll ? renderArray.size() : cache->updateIndices().size(); + buffered_uvs.resize(updateSize); QVector2D uv; uv.setX(0.0f); - for (uint i = 0; i < renderArraySize; i++) { - const ScatterRenderItem &item = renderArray.at(i); + for (int i = 0; i < updateSize; i++) { + int index = updateAll ? i : cache->updateIndices().at(i); + const ScatterRenderItem &item = renderArray.at(index); float y = ((item.translation().y() + m_scaleY) * 0.5f) / m_scaleY; uv.setY(y); diff --git a/src/datavisualization/utils/scatterpointbufferhelper_p.h b/src/datavisualization/utils/scatterpointbufferhelper_p.h index 3edc4c8f..8b34542d 100644 --- a/src/datavisualization/utils/scatterpointbufferhelper_p.h +++ b/src/datavisualization/utils/scatterpointbufferhelper_p.h @@ -46,6 +46,7 @@ public: void pushPoint(uint pointIndex); void popPoint(); void load(ScatterSeriesRenderCache *cache); + void update(ScatterSeriesRenderCache *cache); void setScaleY(float scale) { m_scaleY = scale; } void updateUVs(ScatterSeriesRenderCache *cache); @@ -58,8 +59,7 @@ private: private: QVector m_bufferedPoints; - uint m_oldRemoveIndex; - bool m_oldRemove; + int m_oldRemoveIndex; float m_scaleY; }; -- cgit v1.2.3 From 2788e9f7412fa650f41b25fad1f4b02efb0e5e89 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Fri, 3 Oct 2014 14:08:25 +0300 Subject: Fix static optimization errors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Buffer ids were not cleared appropriately - Update didn't account for items outside axis ranges - In some cases wrong shader was used for point series Task-number: QTRD-3353 Change-Id: Iea465d99eb557402feb5fb017ae92393ee8f377f Reviewed-by: Tomi Korpipää --- src/datavisualization/engine/scatter3drenderer.cpp | 3 ++- src/datavisualization/utils/objecthelper.cpp | 4 ++++ .../utils/scatterobjectbufferhelper.cpp | 24 ++++++++++++++++------ .../utils/scatterpointbufferhelper.cpp | 2 ++ 4 files changed, 26 insertions(+), 7 deletions(-) diff --git a/src/datavisualization/engine/scatter3drenderer.cpp b/src/datavisualization/engine/scatter3drenderer.cpp index b2e0e6e4..19ead4f7 100644 --- a/src/datavisualization/engine/scatter3drenderer.cpp +++ b/src/datavisualization/engine/scatter3drenderer.cpp @@ -887,7 +887,8 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) if (drawingPoints != previousDrawingPoints || (!drawingPoints && (colorStyleIsUniform != (previousMeshColorStyle - == Q3DTheme::ColorStyleUniform)))) { + == Q3DTheme::ColorStyleUniform))) + || (!optimizationDefault && drawingPoints)) { previousDrawingPoints = drawingPoints; if (drawingPoints) { if (!optimizationDefault && rangeGradientPoints) { diff --git a/src/datavisualization/utils/objecthelper.cpp b/src/datavisualization/utils/objecthelper.cpp index a66e0f7e..b64e8c3f 100644 --- a/src/datavisualization/utils/objecthelper.cpp +++ b/src/datavisualization/utils/objecthelper.cpp @@ -122,6 +122,10 @@ void ObjectHelper::load() m_indexedVertices.clear(); m_indexedUVs.clear(); m_indexedNormals.clear(); + m_vertexbuffer = 0; + m_uvbuffer = 0; + m_normalbuffer = 0; + m_elementbuffer = 0; } QVector vertices; QVector uvs; diff --git a/src/datavisualization/utils/scatterobjectbufferhelper.cpp b/src/datavisualization/utils/scatterobjectbufferhelper.cpp index 5eb74011..dae60b96 100644 --- a/src/datavisualization/utils/scatterobjectbufferhelper.cpp +++ b/src/datavisualization/utils/scatterobjectbufferhelper.cpp @@ -58,6 +58,10 @@ void ScatterObjectBufferHelper::fullLoad(ScatterSeriesRenderCache *cache, qreal glDeleteBuffers(1, &m_uvbuffer); glDeleteBuffers(1, &m_normalbuffer); glDeleteBuffers(1, &m_elementbuffer); + m_vertexbuffer = 0; + m_uvbuffer = 0; + m_normalbuffer = 0; + m_elementbuffer = 0; } // Index vertices @@ -314,13 +318,14 @@ void ScatterObjectBufferHelper::update(ScatterSeriesRenderCache *cache, qreal do QVector buffered_vertices; buffered_vertices.resize(verticeCount * updateSize); + int itemCount = 0; for (int i = 0; i < updateSize; i++) { int index = updateAll ? i : cache->updateIndices().at(i); const ScatterRenderItem &item = renderArray.at(index); if (!item.isVisible()) continue; - const int offset = i * verticeCount; + const int offset = itemCount * verticeCount; if (item.rotation().isIdentity()) { for (int j = 0; j < verticeCount; j++) buffered_vertices[j + offset] = scaled_vertices[j] + item.translation(); @@ -334,18 +339,25 @@ void ScatterObjectBufferHelper::update(ScatterSeriesRenderCache *cache, qreal do buffered_vertices[j + offset] = indexed_vertices[j] * modelMatrix + item.translation(); } + itemCount++; } glBindBuffer(GL_ARRAY_BUFFER, m_vertexbuffer); + int sizeOfItem = verticeCount * sizeof(QVector3D); if (updateAll) { - glBufferData(GL_ARRAY_BUFFER, buffered_vertices.size() * sizeof(QVector3D), - &buffered_vertices.at(0), GL_DYNAMIC_DRAW); + if (itemCount) { + glBufferData(GL_ARRAY_BUFFER, itemCount * sizeOfItem, + &buffered_vertices.at(0), GL_STATIC_DRAW); + } } else { - int itemSize = verticeCount * sizeof(QVector3D); + itemCount = 0; for (int i = 0; i < updateSize; i++) { int index = updateAll ? i : cache->updateIndices().at(i); - glBufferSubData(GL_ARRAY_BUFFER, cache->bufferIndices().at(index) * itemSize, - itemSize, &buffered_vertices.at(i * verticeCount)); + if (renderArray.at(index).isVisible()) { + glBufferSubData(GL_ARRAY_BUFFER, cache->bufferIndices().at(index) * sizeOfItem, + sizeOfItem, &buffered_vertices.at(itemCount * verticeCount)); + itemCount++; + } } } glBindBuffer(GL_ARRAY_BUFFER, 0); diff --git a/src/datavisualization/utils/scatterpointbufferhelper.cpp b/src/datavisualization/utils/scatterpointbufferhelper.cpp index 5076502f..f15ce3ec 100644 --- a/src/datavisualization/utils/scatterpointbufferhelper.cpp +++ b/src/datavisualization/utils/scatterpointbufferhelper.cpp @@ -87,6 +87,8 @@ void ScatterPointBufferHelper::load(ScatterSeriesRenderCache *cache) glDeleteBuffers(1, &m_pointbuffer); glDeleteBuffers(1, &m_uvbuffer); m_bufferedPoints.clear(); + m_pointbuffer = 0; + m_uvbuffer = 0; } bool itemsVisible = false; -- cgit v1.2.3 From 90ac955006749ecf6ea021bc7cf736a6c21095af Mon Sep 17 00:00:00 2001 From: Mika Salmela Date: Fri, 3 Oct 2014 16:47:44 +0300 Subject: Fix surface normals on ascending/descending MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Normals and triangles are generated according to data direction. Task-number: QTRD-3160 Change-Id: Ia4286467682628930da3d82b204fd1ec078a396a Reviewed-by: Miikka Heikkinen Reviewed-by: Tomi Korpipää --- src/datavisualization/utils/surfaceobject.cpp | 555 +++++++++++++++++--------- src/datavisualization/utils/surfaceobject_p.h | 24 +- tests/surfacetest/graphmodifier.cpp | 43 +- tests/surfacetest/graphmodifier.h | 2 + 4 files changed, 416 insertions(+), 208 deletions(-) diff --git a/src/datavisualization/utils/surfaceobject.cpp b/src/datavisualization/utils/surfaceobject.cpp index f8dae6f4..c91075e5 100644 --- a/src/datavisualization/utils/surfaceobject.cpp +++ b/src/datavisualization/utils/surfaceobject.cpp @@ -32,7 +32,9 @@ SurfaceObject::SurfaceObject(Surface3DRenderer *renderer) m_axisCacheY(renderer->m_axisCacheY), m_axisCacheZ(renderer->m_axisCacheZ), m_renderer(renderer), - m_returnTextureBuffer(false) + m_returnTextureBuffer(false), + m_dataDimension(0), + m_oldDataDimension(-1) { m_indicesType = GL_UNSIGNED_INT; initializeOpenGLFunctions(); @@ -63,6 +65,12 @@ void SurfaceObject::setUpSmoothData(const QSurfaceDataArray &dataArray, const QR m_surfaceType = SurfaceSmooth; + checkDirections(dataArray); + bool indicesDirty = false; + if (m_dataDimension != m_oldDataDimension) + indicesDirty = true; + m_oldDataDimension = m_dataDimension; + // Create/populate vertix table if (changeGeometry) m_vertices.resize(totalSize); @@ -96,46 +104,198 @@ void SurfaceObject::setUpSmoothData(const QSurfaceDataArray &dataArray, const QR // Create normals int rowLimit = m_rows - 1; int colLimit = m_columns - 1; - int rowColLimit = rowLimit * m_columns; - int totalLimit = totalSize - 1; if (changeGeometry) m_normals.resize(totalSize); totalIndex = 0; - const bool flipNormal = checkFlipNormal(dataArray); - for (int row = 0; row < rowColLimit; row += m_columns) { - for (int j = 0; j < colLimit; j++) { - m_normals[totalIndex++] = normal(m_vertices.at(row + j), - m_vertices.at(row + j + 1), - m_vertices.at(row + m_columns + j), - flipNormal); - } - int p = row + colLimit; - m_normals[totalIndex++] = normal(m_vertices.at(p), - m_vertices.at(p + m_columns), - m_vertices.at(p - 1), - flipNormal); - } - for (int j = rowColLimit; j < totalLimit; j++) { - m_normals[totalIndex++] = normal(m_vertices.at(j), - m_vertices.at(j - m_columns), - m_vertices.at(j + 1), - flipNormal); + + if ((m_dataDimension == BothAscending) || (m_dataDimension == XDescending)) { + for (int row = 0; row < rowLimit; row ++) + createSmoothNormalBodyLine(totalIndex, row * m_columns); + createSmoothNormalUpperLine(totalIndex); + } else { // BothDescending || ZDescending + createSmoothNormalUpperLine(totalIndex); + for (int row = 1; row < m_rows; row++) + createSmoothNormalBodyLine(totalIndex, row * m_columns); } - m_normals[totalIndex++] = normal(m_vertices.at(totalLimit), - m_vertices.at(totalLimit - 1), - m_vertices.at(totalLimit - m_columns), - flipNormal); // Create indices table - if (changeGeometry) + if (changeGeometry || indicesDirty) createSmoothIndices(0, 0, colLimit, rowLimit); // Create line element indices if (changeGeometry) createSmoothGridlineIndices(0, 0, colLimit, rowLimit); - createBuffers(m_vertices, uvs, m_normals, 0, changeGeometry); + createBuffers(m_vertices, uvs, m_normals, 0); +} + +void SurfaceObject::createSmoothNormalBodyLine(int &totalIndex, int column) +{ + int colLimit = m_columns - 1; + + if (m_dataDimension == BothAscending) { + int end = colLimit + column; + for (int j = column; j < end; j++) { + m_normals[totalIndex++] = normal(m_vertices.at(j), + m_vertices.at(j + 1), + m_vertices.at(j + m_columns)); + } + m_normals[totalIndex++] = normal(m_vertices.at(end), + m_vertices.at(end + m_columns), + m_vertices.at(end - 1)); + } else if (m_dataDimension == XDescending) { + m_normals[totalIndex++] = normal(m_vertices.at(column), + m_vertices.at(column + m_columns), + m_vertices.at(column + 1)); + int end = column + m_columns; + for (int j = column + 1; j < end; j++) { + m_normals[totalIndex++] = normal(m_vertices.at(j), + m_vertices.at(j - 1), + m_vertices.at(j + m_columns)); + } + } else if (m_dataDimension == ZDescending) { + int end = colLimit + column; + for (int j = column; j < end; j++) { + m_normals[totalIndex++] = normal(m_vertices.at(j), + m_vertices.at(j + 1), + m_vertices.at(j - m_columns)); + } + m_normals[totalIndex++] = normal(m_vertices.at(end), + m_vertices.at(end - m_columns), + m_vertices.at(end - 1)); + } else { // BothDescending + m_normals[totalIndex++] = normal(m_vertices.at(column), + m_vertices.at(column - m_columns), + m_vertices.at(column + 1)); + int end = column + m_columns; + for (int j = column + 1; j < end; j++) { + m_normals[totalIndex++] = normal(m_vertices.at(j), + m_vertices.at(j - 1), + m_vertices.at(j - m_columns)); + } + } +} + +void SurfaceObject::createSmoothNormalUpperLine(int &totalIndex) +{ + if ((m_dataDimension == BothAscending) ) { + int lineEnd = m_rows * m_columns - 1; + for (int j = (m_rows - 1) * m_columns; j < lineEnd; j++) { + m_normals[totalIndex++] = normal(m_vertices.at(j), + m_vertices.at(j - m_columns), + m_vertices.at(j + 1)); + } + m_normals[totalIndex++] = normal(m_vertices.at(lineEnd), + m_vertices.at(lineEnd - 1), + m_vertices.at(lineEnd - m_columns)); + } else if (m_dataDimension == XDescending) { + int lineStart = (m_rows - 1) * m_columns; + int lineEnd = m_rows * m_columns; + m_normals[totalIndex++] = normal(m_vertices.at(lineStart), + m_vertices.at(lineStart + 1), + m_vertices.at(lineStart - m_columns)); + for (int j = lineStart + 1; j < lineEnd; j++) { + m_normals[totalIndex++] = normal(m_vertices.at(j), + m_vertices.at(j - m_columns), + m_vertices.at(j - 1)); + } + } else if (m_dataDimension == ZDescending) { + int colLimit = m_columns - 1; + for (int j = 0; j < colLimit; j++) { + m_normals[totalIndex++] = normal(m_vertices.at(j), + m_vertices.at(j + m_columns), + m_vertices.at(j + 1)); + } + m_normals[totalIndex++] = normal(m_vertices.at(colLimit), + m_vertices.at(colLimit - 1), + m_vertices.at(colLimit + m_columns)); + } else { // BothDescending + m_normals[totalIndex++] = normal(m_vertices.at(0), + m_vertices.at(1), + m_vertices.at(m_columns)); + for (int j = 1; j < m_columns; j++) { + m_normals[totalIndex++] = normal(m_vertices.at(j), + m_vertices.at(j + m_columns), + m_vertices.at(j - 1)); + } + } +} + +QVector3D SurfaceObject::createSmoothNormalBodyLineItem(int x, int y) +{ + int p = y * m_columns + x; + if (m_dataDimension == BothAscending) { + if (x < m_columns - 1) { + return normal(m_vertices.at(p), m_vertices.at(p + 1), + m_vertices.at(p + m_columns)); + } else { + return normal(m_vertices.at(p), m_vertices.at(p + m_columns), + m_vertices.at(p - 1)); + } + } else if (m_dataDimension == XDescending) { + if (x == 0) { + return normal(m_vertices.at(p), m_vertices.at(p + m_columns), + m_vertices.at(p + 1)); + } else { + return normal(m_vertices.at(p), m_vertices.at(p - 1), + m_vertices.at(p + m_columns)); + } + } else if (m_dataDimension == ZDescending) { + if (x < m_columns - 1) { + return normal(m_vertices.at(p), m_vertices.at(p + 1), + m_vertices.at(p - m_columns)); + } else { + return normal(m_vertices.at(p), m_vertices.at(p - m_columns), + m_vertices.at(p - 1)); + } + } else { // BothDescending + if (x == 0) { + return normal(m_vertices.at(p), m_vertices.at(p - m_columns), + m_vertices.at(p + 1)); + } else { + return normal(m_vertices.at(p), m_vertices.at(p - 1), + m_vertices.at(p - m_columns)); + } + } +} + +QVector3D SurfaceObject::createSmoothNormalUpperLineItem(int x, int y) +{ + int p = y * m_columns + x; + if ((m_dataDimension == BothAscending) ) { + if (x < m_columns - 1) { + return normal(m_vertices.at(p), m_vertices.at(p - m_columns), + m_vertices.at(p + 1)); + } else { + return normal(m_vertices.at(p), m_vertices.at(p - 1), + m_vertices.at(p - m_columns)); + } + } else if (m_dataDimension == XDescending) { + if (x == 0) { + return normal(m_vertices.at(p), m_vertices.at(p + 1), + m_vertices.at(p - m_columns)); + } else { + return normal(m_vertices.at(p), m_vertices.at(p - m_columns), + m_vertices.at(p - 1)); + } + } else if (m_dataDimension == ZDescending) { + if (x < m_columns - 1) { + return normal(m_vertices.at(p), m_vertices.at(p + m_columns), + m_vertices.at(p + 1)); + } else { + return normal(m_vertices.at(p), m_vertices.at(p - 1), + m_vertices.at(p + m_columns)); + } + } else { // BothDescending + if (x == 0) { + return normal(m_vertices.at(0), m_vertices.at(1), + m_vertices.at(m_columns)); + } else { + return normal(m_vertices.at(p), m_vertices.at(p + m_columns), + m_vertices.at(p - 1)); + } + } } void SurfaceObject::smoothUVs(const QSurfaceDataArray &dataArray, @@ -179,48 +339,27 @@ void SurfaceObject::updateSmoothRow(const QSurfaceDataArray &dataArray, int rowI getNormalizedVertex(dataRow.at(j), m_vertices[p++], polar, false); // Create normals - int colLimit = m_columns - 1; + bool upwards = (m_dataDimension == BothAscending) || (m_dataDimension == XDescending); int startRow = rowIndex; - if (startRow > 0) + if ((startRow > 0) && upwards) startRow--; + int endRow = rowIndex; + if (!upwards && (rowIndex < m_rows - 1)) + endRow++; + if ((endRow == m_rows - 1) && upwards) + endRow--; int totalIndex = startRow * m_columns; - int rowLimit = (rowIndex + 1) * m_columns; - if (rowIndex == m_rows - 1) - rowLimit = rowIndex * m_columns; // The rowIndex is top most row, special handling - const bool flipNormal = checkFlipNormal(dataArray); - for (int row = totalIndex; row < rowLimit; row += m_columns) { - for (int j = 0; j < colLimit; j++) { - // One right and one up - m_normals[totalIndex++] = normal(m_vertices.at(row + j), - m_vertices.at(row + j + 1), - m_vertices.at(row + m_columns + j), - flipNormal); - } - int p = row + colLimit; - // One up and one left - m_normals[totalIndex++] = normal(m_vertices.at(p), - m_vertices.at(p + m_columns), - m_vertices.at(p - 1), - flipNormal); + if ((startRow == 0) && !upwards) { + createSmoothNormalUpperLine(totalIndex); + startRow++; } - if (rowIndex == m_rows - 1) { - // Top most line, nothing above, must have different handling. - // Take from one down and one right. Read till second-to-last - rowLimit = (rowIndex + 1) * m_columns - 1; - for (int j = rowIndex * m_columns; j < rowLimit; j++) { - m_normals[totalIndex++] = normal(m_vertices.at(j), - m_vertices.at(j - m_columns), - m_vertices.at(j + 1), - flipNormal); - } - // Top left corner. Take from one left and one down - m_normals[totalIndex++] = normal(m_vertices.at(rowLimit), - m_vertices.at(rowLimit - 1), - m_vertices.at(rowLimit - m_columns), - flipNormal); - } + for (int row = startRow; row <= endRow; row++) + createSmoothNormalBodyLine(totalIndex, row * m_columns); + + if ((rowIndex == m_rows - 1) && upwards) + createSmoothNormalUpperLine(totalIndex); } void SurfaceObject::updateSmoothItem(const QSurfaceDataArray &dataArray, int row, int column, @@ -231,50 +370,33 @@ void SurfaceObject::updateSmoothItem(const QSurfaceDataArray &dataArray, int row m_vertices[row * m_columns + column], polar, false); // Create normals + bool upwards = (m_dataDimension == BothAscending) || (m_dataDimension == XDescending); + bool rightwards = (m_dataDimension == BothAscending) || (m_dataDimension == ZDescending); int startRow = row; - if (startRow > 0) - startRow--; // Change the normal for previous row also + if ((startRow > 0) && upwards) + startRow--; + int endRow = row; + if (!upwards && (row < m_rows - 1)) + endRow++; + if ((endRow == m_rows - 1) && upwards) + endRow--; int startCol = column; - if (startCol > 0) + if ((startCol > 0) && rightwards) startCol--; - int rightCol = m_columns - 1; - int topRow = m_rows - 1; + int endCol = column; + if ((endCol < m_columns - 1) && !rightwards) + endCol++; - const bool flipNormal = checkFlipNormal(dataArray); - for (int i = startRow; i <= row; i++) { - for (int j = startCol; j <= column; j++) { + for (int i = startRow; i <= endRow; i++) { + for (int j = startCol; j <= endCol; j++) { int p = i * m_columns + j; - if (i < topRow) { - if (j < rightCol) { - // One right and one up - m_normals[p] = normal(m_vertices.at(p), - m_vertices.at(p + 1), - m_vertices.at(p + m_columns), - flipNormal); - } else { - // Last item, nothing on the right. One up and one left - m_normals[p] = normal(m_vertices.at(p), - m_vertices.at(p + m_columns), - m_vertices.at(p - 1), - flipNormal); - } - } else { - // Top most line, nothing above, must have different handling. - if (j < rightCol) { - // Take from one down and one right. Read till second-to-last - m_normals[p] = normal(m_vertices.at(p), - m_vertices.at(p - m_columns), - m_vertices.at(p + 1), - flipNormal); - } else { - // Top left corner. Take from one left and one down - m_normals[p] = normal(m_vertices.at(p), - m_vertices.at(p - 1), - m_vertices.at(p - m_columns), - flipNormal); - } - } - } + if ((i == 0) && !upwards) + m_normals[p] = createSmoothNormalUpperLineItem(j, i); + else if ((i == m_rows - 1) && upwards) + m_normals[p] = createSmoothNormalUpperLineItem(j, i); + else + m_normals[p] = createSmoothNormalBodyLineItem(j, i); + } } } @@ -296,15 +418,38 @@ void SurfaceObject::createSmoothIndices(int x, int y, int endX, int endY) int rowEnd = endY * m_columns; for (int row = y * m_columns; row < rowEnd; row += m_columns) { for (int j = x; j < endX; j++) { - // Left triangle - indices[p++] = row + j + 1; - indices[p++] = row + m_columns + j; - indices[p++] = row + j; - - // Right triangle - indices[p++] = row + m_columns + j + 1; - indices[p++] = row + m_columns + j; - indices[p++] = row + j + 1; + if ((m_dataDimension == BothAscending) || (m_dataDimension == BothDescending)) { + // Left triangle + indices[p++] = row + j + 1; + indices[p++] = row + m_columns + j; + indices[p++] = row + j; + + // Right triangle + indices[p++] = row + m_columns + j + 1; + indices[p++] = row + m_columns + j; + indices[p++] = row + j + 1; + } else if (m_dataDimension == XDescending) { + // Right triangle + indices[p++] = row + m_columns + j; + indices[p++] = row + m_columns + j + 1; + indices[p++] = row + j; + + // Left triangle + indices[p++] = row + j; + indices[p++] = row + m_columns + j + 1; + indices[p++] = row + j + 1; + } else { + // Left triangle + indices[p++] = row + m_columns + j; + indices[p++] = row + m_columns + j + 1; + indices[p++] = row + j; + + // Right triangle + indices[p++] = row + j; + indices[p++] = row + m_columns + j + 1; + indices[p++] = row + j + 1; + + } } } @@ -364,6 +509,12 @@ void SurfaceObject::setUpData(const QSurfaceDataArray &dataArray, const QRect &s GLfloat uvX = 1.0f / GLfloat(m_columns - 1); GLfloat uvY = 1.0f / GLfloat(m_rows - 1); + checkDirections(dataArray); + bool indicesDirty = false; + if (m_dataDimension != m_oldDataDimension) + indicesDirty = true; + m_oldDataDimension = m_dataDimension; + m_surfaceType = SurfaceFlat; // Create vertix table @@ -411,42 +562,23 @@ void SurfaceObject::setUpData(const QSurfaceDataArray &dataArray, const QRect &s // Create normals & indices table GLint *indices = 0; - int p = 0; - if (changeGeometry) { + if (changeGeometry || indicesDirty) { int normalCount = 2 * colLimit * rowLimit; m_indexCount = 3 * normalCount; indices = new GLint[m_indexCount]; m_normals.resize(normalCount); } + int p = 0; totalIndex = 0; - const bool flipNormal = checkFlipNormal(dataArray); for (int row = 0, upperRow = doubleColumns; row < rowColLimit; row += doubleColumns, upperRow += doubleColumns) { for (int j = 0; j < doubleColumns; j += 2) { - // Normal for the left triangle - m_normals[totalIndex++] = normal(m_vertices.at(row + j), - m_vertices.at(row + j + 1), - m_vertices.at(upperRow + j), - flipNormal); - - // Normal for the right triangle - m_normals[totalIndex++] = normal(m_vertices.at(row + j + 1), - m_vertices.at(upperRow + j + 1), - m_vertices.at(upperRow + j), - flipNormal); - - if (changeGeometry) { - // Left triangle - indices[p++] = row + j + 1; - indices[p++] = upperRow + j; - indices[p++] = row + j; + createNormals(totalIndex, row, upperRow, j); - // Right triangle - indices[p++] = upperRow + j + 1; - indices[p++] = upperRow + j; - indices[p++] = row + j + 1; + if (changeGeometry || indicesDirty) { + createCoarseIndices(indices, p, row, upperRow, j); } } } @@ -455,7 +587,7 @@ void SurfaceObject::setUpData(const QSurfaceDataArray &dataArray, const QRect &s if (changeGeometry) createCoarseGridlineIndices(0, 0, colLimit, rowLimit); - createBuffers(m_vertices, uvs, m_normals, indices, changeGeometry); + createBuffers(m_vertices, uvs, m_normals, indices); delete[] indices; } @@ -519,23 +651,11 @@ void SurfaceObject::updateCoarseRow(const QSurfaceDataArray &dataArray, int rowI int rowLimit = (rowIndex + 1) * doubleColumns; if (rowIndex == m_rows - 1) rowLimit = rowIndex * doubleColumns; //Topmost row, no normals - const bool flipNormal = checkFlipNormal(dataArray); for (int row = p, upperRow = p + doubleColumns; row < rowLimit; row += doubleColumns, upperRow += doubleColumns) { - for (int j = 0; j < doubleColumns; j += 2) { - // Normal for the left triangle - m_normals[p++] = normal(m_vertices.at(row + j), - m_vertices.at(row + j + 1), - m_vertices.at(upperRow + j), - flipNormal); - - // Normal for the right triangle - m_normals[p++] = normal(m_vertices.at(row + j + 1), - m_vertices.at(upperRow + j + 1), - m_vertices.at(upperRow + j), - flipNormal); - } + for (int j = 0; j < doubleColumns; j += 2) + createNormals(p, row, upperRow, j); } } @@ -564,27 +684,15 @@ void SurfaceObject::updateCoarseItem(const QSurfaceDataArray &dataArray, int row if (column == m_columns - 1) column--; - const bool flipNormal = checkFlipNormal(dataArray); for (int i = startRow; i <= row; i++) { for (int j = startCol; j <= column; j++) { p = i * doubleColumns + j * 2; - // Normal for the left triangle - m_normals[p] = normal(m_vertices.at(p), - m_vertices.at(p + 1), - m_vertices.at(p + doubleColumns), - flipNormal); - p++; - - // Normal for the right triangle - m_normals[p] = normal(m_vertices.at(p), - m_vertices.at(p + doubleColumns), - m_vertices.at(p + doubleColumns - 1), - flipNormal); + createNormals(p, i * doubleColumns, (i + 1) * doubleColumns, j * 2); } } } -void SurfaceObject::createCoarseIndices(int x, int y, int columns, int rows) +void SurfaceObject::createCoarseSubSection(int x, int y, int columns, int rows) { if (columns > m_columns) columns = m_columns; @@ -606,17 +714,8 @@ void SurfaceObject::createCoarseIndices(int x, int y, int columns, int rows) for (int row = y * doubleColumns, upperRow = (y + 1) * doubleColumns; row < rowColLimit; row += doubleColumns, upperRow += doubleColumns) { - for (int j = 2 * x; j < doubleColumnsLimit; j += 2) { - // Left triangle - indices[p++] = row + j + 1; - indices[p++] = upperRow + j; - indices[p++] = row + j; - - // Right triangle - indices[p++] = upperRow + j + 1; - indices[p++] = upperRow + j; - indices[p++] = row + j + 1; - } + for (int j = 2 * x; j < doubleColumnsLimit; j += 2) + createCoarseIndices(indices, p, row, upperRow, j); } glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_elementbuffer); @@ -680,12 +779,11 @@ void SurfaceObject::createCoarseGridlineIndices(int x, int y, int endX, int endY void SurfaceObject::uploadBuffers() { QVector uvs; // Empty dummy - createBuffers(m_vertices, uvs, m_normals, 0, false); + createBuffers(m_vertices, uvs, m_normals, 0); } void SurfaceObject::createBuffers(const QVector &vertices, const QVector &uvs, - const QVector &normals, const GLint *indices, - bool changeGeometry) + const QVector &normals, const GLint *indices) { // Move to buffers glBindBuffer(GL_ARRAY_BUFFER, m_vertexbuffer); @@ -696,30 +794,37 @@ void SurfaceObject::createBuffers(const QVector &vertices, const QVec glBufferData(GL_ARRAY_BUFFER, normals.size() * sizeof(QVector3D), &normals.at(0), GL_DYNAMIC_DRAW); - if (changeGeometry) { + if (uvs.size()) { glBindBuffer(GL_ARRAY_BUFFER, m_uvbuffer); glBufferData(GL_ARRAY_BUFFER, uvs.size() * sizeof(QVector2D), &uvs.at(0), GL_STATIC_DRAW); + } - if (indices) { - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_elementbuffer); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, m_indexCount * sizeof(GLint), - indices, GL_STATIC_DRAW); - } - - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + if (indices) { + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_elementbuffer); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, m_indexCount * sizeof(GLint), + indices, GL_STATIC_DRAW); } + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); glBindBuffer(GL_ARRAY_BUFFER, 0); m_meshDataLoaded = true; } -bool SurfaceObject::checkFlipNormal(const QSurfaceDataArray &array) +void SurfaceObject::checkDirections(const QSurfaceDataArray &array) { - const bool ascendingX = array.at(0)->at(0).x() < array.at(0)->at(array.at(0)->size() - 1).x(); - const bool ascendingZ = array.at(0)->at(0).z() < array.at(array.size() - 1)->at(0).z(); - return ascendingX != ascendingZ; + m_dataDimension = BothAscending; + + if (array.at(0)->at(0).x() > array.at(0)->at(array.at(0)->size() - 1).x()) + m_dataDimension |= XDescending; + if (m_axisCacheX.reversed()) + m_dataDimension ^= XDescending; + + if (array.at(0)->at(0).z() > array.at(array.size() - 1)->at(0).z()) + m_dataDimension |= ZDescending; + if (m_axisCacheZ.reversed()) + m_dataDimension ^= ZDescending; } void SurfaceObject::getNormalizedVertex(const QSurfaceDataItem &data, QVector3D &vertex, @@ -792,14 +897,74 @@ void SurfaceObject::clear() m_normals.clear(); } -QVector3D SurfaceObject::normal(const QVector3D &a, const QVector3D &b, const QVector3D &c, - bool flipNormal) +void SurfaceObject::createCoarseIndices(GLint *indices, int &p, int row, int upperRow, int j) +{ + if ((m_dataDimension == BothAscending) || (m_dataDimension == BothDescending)) { + // Left triangle + indices[p++] = row + j + 1; + indices[p++] = upperRow + j; + indices[p++] = row + j; + + // Right triangle + indices[p++] = upperRow + j + 1; + indices[p++] = upperRow + j; + indices[p++] = row + j + 1; + } else if (m_dataDimension == XDescending) { + indices[p++] = upperRow + j; + indices[p++] = upperRow + j + 1; + indices[p++] = row + j; + + indices[p++] = row + j; + indices[p++] = upperRow + j + 1; + indices[p++] = row + j + 1; + } else { + // Left triangle + indices[p++] = upperRow + j; + indices[p++] = upperRow + j + 1; + indices[p++] = row + j; + + // Right triangle + indices[p++] = row + j; + indices[p++] = upperRow + j + 1; + indices[p++] = row + j + 1; + } +} + +void SurfaceObject::createNormals(int &p, int row, int upperRow, int j) +{ + if ((m_dataDimension == BothAscending) || (m_dataDimension == BothDescending)) { + m_normals[p++] = normal(m_vertices.at(row + j), + m_vertices.at(row + j + 1), + m_vertices.at(upperRow + j)); + + m_normals[p++] = normal(m_vertices.at(row + j + 1), + m_vertices.at(upperRow + j + 1), + m_vertices.at(upperRow + j)); + } else if (m_dataDimension == XDescending) { + m_normals[p++] = normal(m_vertices.at(row + j), + m_vertices.at(upperRow + j), + m_vertices.at(upperRow + j + 1)); + + m_normals[p++] = normal(m_vertices.at(row + j + 1), + m_vertices.at(row + j), + m_vertices.at(upperRow + j + 1)); + } else { + m_normals[p++] = normal(m_vertices.at(row + j), + m_vertices.at(upperRow + j), + m_vertices.at(upperRow + j + 1)); + + m_normals[p++] = normal(m_vertices.at(row + j + 1), + m_vertices.at(row + j), + m_vertices.at(upperRow + j + 1)); + } +} + +QVector3D SurfaceObject::normal(const QVector3D &a, const QVector3D &b, const QVector3D &c) { QVector3D v1 = b - a; QVector3D v2 = c - a; QVector3D normal = QVector3D::crossProduct(v1, v2); - if (flipNormal) - normal *= -1; + return normal; } diff --git a/src/datavisualization/utils/surfaceobject_p.h b/src/datavisualization/utils/surfaceobject_p.h index f373eb5d..39d10445 100644 --- a/src/datavisualization/utils/surfaceobject_p.h +++ b/src/datavisualization/utils/surfaceobject_p.h @@ -49,6 +49,13 @@ public: Undefined }; + enum DataDimensions { + BothAscending = 0, + XDescending = 1, + ZDescending = 2, + BothDescending = XDescending | ZDescending + }; + public: SurfaceObject(Surface3DRenderer *renderer); virtual ~SurfaceObject(); @@ -64,7 +71,7 @@ public: void updateSmoothItem(const QSurfaceDataArray &dataArray, int row, int column, bool polar); void updateCoarseItem(const QSurfaceDataArray &dataArray, int row, int column, bool polar); void createSmoothIndices(int x, int y, int endX, int endY); - void createCoarseIndices(int x, int y, int columns, int rows); + void createCoarseSubSection(int x, int y, int columns, int rows); void createSmoothGridlineIndices(int x, int y, int endX, int endY); void createCoarseGridlineIndices(int x, int y, int endX, int endY); void uploadBuffers(); @@ -78,11 +85,16 @@ public: inline void activateSurfaceTexture(bool value) { m_returnTextureBuffer = value; } private: - QVector3D normal(const QVector3D &a, const QVector3D &b, const QVector3D &c, bool flipNormal); + void createCoarseIndices(GLint *indices, int &p, int row, int upperRow, int j); + void createNormals(int &p, int row, int upperRow, int j); + void createSmoothNormalBodyLine(int &totalIndex, int column); + void createSmoothNormalUpperLine(int &totalIndex); + QVector3D createSmoothNormalBodyLineItem(int x, int y); + QVector3D createSmoothNormalUpperLineItem(int x, int y); + QVector3D normal(const QVector3D &a, const QVector3D &b, const QVector3D &c); void createBuffers(const QVector &vertices, const QVector &uvs, - const QVector &normals, const GLint *indices, - bool changeGeometry); - bool checkFlipNormal(const QSurfaceDataArray &array); + const QVector &normals, const GLint *indices); + void checkDirections(const QSurfaceDataArray &array); inline void getNormalizedVertex(const QSurfaceDataItem &data, QVector3D &vertex, bool polar, bool flipXZ); @@ -103,6 +115,8 @@ private: float m_maxY; GLuint m_uvTextureBuffer; bool m_returnTextureBuffer; + int m_dataDimension; + int m_oldDataDimension; }; QT_END_NAMESPACE_DATAVISUALIZATION diff --git a/tests/surfacetest/graphmodifier.cpp b/tests/surfacetest/graphmodifier.cpp index 2d0e9357..5d8183b0 100644 --- a/tests/surfacetest/graphmodifier.cpp +++ b/tests/surfacetest/graphmodifier.cpp @@ -67,7 +67,9 @@ GraphModifier::GraphModifier(Q3DSurface *graph, QWidget *parentWidget) m_drawMode3(QSurface3DSeries::DrawSurfaceAndWireframe), m_drawMode4(QSurface3DSeries::DrawSurfaceAndWireframe), m_offset(4.0f), - m_parentWidget(parentWidget) + m_parentWidget(parentWidget), + m_ascendingX(true), + m_ascendingZ(true) { m_graph->setAxisX(new QValue3DAxis); m_graph->axisX()->setTitle("X-Axis"); @@ -753,6 +755,8 @@ void GraphModifier::toggleAxisTitleFixed(bool enabled) void GraphModifier::toggleXAscending(bool enabled) { + m_ascendingX = enabled; + // Flip data array contents if necessary foreach (QSurface3DSeries *series, m_graph->seriesList()) { QSurfaceDataArray *array = const_cast(series->dataProxy()->array()); @@ -781,6 +785,8 @@ void GraphModifier::toggleXAscending(bool enabled) void GraphModifier::toggleZAscending(bool enabled) { + m_ascendingZ = enabled; + // Flip data array contents if necessary foreach (QSurface3DSeries *series, m_graph->seriesList()) { QSurfaceDataArray *array = const_cast(series->dataProxy()->array()); @@ -896,7 +902,10 @@ void GraphModifier::changeRow() int row = rand() % m_zCount; QSurfaceDataRow *newRow = createMultiRow(row, changeRowSeries, true); - m_multiseries[changeRowSeries]->dataProxy()->setRow(row, newRow); + if (m_ascendingZ) + m_multiseries[changeRowSeries]->dataProxy()->setRow(row, newRow); + else + m_multiseries[changeRowSeries]->dataProxy()->setRow((m_zCount - 1) - row, newRow); changeRowSeries++; if (changeRowSeries > 3) @@ -913,11 +922,20 @@ QSurfaceDataRow *GraphModifier::createMultiRow(int row, int series, bool change) float i = float(row); QSurfaceDataRow *newRow = new QSurfaceDataRow(m_xCount); float z = float(i) - m_limitZ + 0.5f + m_multiSampleOffsetZ[series]; - for (int j = 0; j < m_xCount; j++) { - float x = float(j) - m_limitX + 0.5f + m_multiSampleOffsetX[series]; - float angle = (z * x) / float(full) * 1.57f; - float y = qSin(angle * float(qPow(1.3f, series))) + 0.2f * float(change) + 1.1f *series; - (*newRow)[j].setPosition(QVector3D(x, y, z)); + if (m_ascendingX) { + for (int j = 0; j < m_xCount; j++) { + float x = float(j) - m_limitX + 0.5f + m_multiSampleOffsetX[series]; + float angle = (z * x) / float(full) * 1.57f; + float y = qSin(angle * float(qPow(1.3f, series))) + 0.2f * float(change) + 1.1f *series; + (*newRow)[j].setPosition(QVector3D(x, y, z)); + } + } else { + for (int j = m_xCount - 1; j >= 0 ; j--) { + float x = float(j) - m_limitX + 0.5f + m_multiSampleOffsetX[series]; + float angle = (z * x) / float(full) * 1.57f; + float y = qSin(angle * float(qPow(1.3f, series))) + 0.2f * float(change) + 1.1f *series; + (*newRow)[(m_xCount - 1) - j].setPosition(QVector3D(x, y, z)); + } } return newRow; @@ -1026,7 +1044,16 @@ void GraphModifier::changeItem() float angle = (z * x) / float(full) * 1.57f; float y = qSin(angle * float(qPow(1.3f, changeItemSeries))) + 0.2f + 1.1f *changeItemSeries; QSurfaceDataItem newItem(QVector3D(x, y, z)); - m_multiseries[changeItemSeries]->dataProxy()->setItem(int(i), int(j), newItem); + + if (m_ascendingZ && m_ascendingX) + m_multiseries[changeItemSeries]->dataProxy()->setItem(int(i), int(j), newItem); + else if (!m_ascendingZ && m_ascendingX) + m_multiseries[changeItemSeries]->dataProxy()->setItem(m_zCount - 1 - int(i), int(j), newItem); + else if (m_ascendingZ && !m_ascendingX) + m_multiseries[changeItemSeries]->dataProxy()->setItem(int(i), m_xCount - 1 - int(j), newItem); + else + m_multiseries[changeItemSeries]->dataProxy()->setItem(m_zCount - 1 - int(i), m_xCount - 1 - int(j), newItem); + //m_multiseries[changeItemSeries]->setSelectedPoint(QPoint(i, j)); changeItemSeries++; if (changeItemSeries > 3) changeItemSeries = 0; diff --git a/tests/surfacetest/graphmodifier.h b/tests/surfacetest/graphmodifier.h index f85fbc15..223a6854 100644 --- a/tests/surfacetest/graphmodifier.h +++ b/tests/surfacetest/graphmodifier.h @@ -194,6 +194,8 @@ private: QVector3D m_cameraTarget; QWidget *m_parentWidget; QTimer m_graphPositionQueryTimer; + bool m_ascendingX; + bool m_ascendingZ; }; #endif -- cgit v1.2.3 From e42580773cb4124ff02669b8a87ff550057d3793 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Mon, 6 Oct 2014 11:10:14 +0300 Subject: Fix mac -> OS X MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: If0409af049a55dae152139fdb55a2eb58e5fbb51 Reviewed-by: Tomi Korpipää --- README | 4 ++-- dist/changes-1.1.0 | 2 +- examples/datavisualization/audiolevels/audiolevels.cpp | 2 +- src/datavisualization/datavisualization.pro | 2 +- src/datavisualization/doc/src/qtdatavisualization.qdoc | 4 ++-- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/README b/README index b06437ce..357507d5 100644 --- a/README +++ b/README @@ -22,7 +22,7 @@ After running qmake, build the project with make: (Linux) make (Windows with MinGw) mingw32-make (Windows with Visual Studio) nmake - (OSX) make + (OS X) make The above generates the default makefiles for your configuration, which is typically the release build if you are using precompiled binary Qt distribution. To build both @@ -42,7 +42,7 @@ For release builds: qmake CONFIG+=debug_and_release make release -For both builds (Windows/Mac only): +For both builds (Windows/OS X only): qmake CONFIG+="debug_and_release build_all" make diff --git a/dist/changes-1.1.0 b/dist/changes-1.1.0 index 7d27438b..d404cf09 100644 --- a/dist/changes-1.1.0 +++ b/dist/changes-1.1.0 @@ -73,4 +73,4 @@ Platform specific changes - Fixed issue with graph not always updating before rotating the graph in iOS. - Fixed shader linking error on some Android versions. -- Fixed memory leaks in Mac and Android builds. +- Fixed memory leaks in OS X and Android builds. diff --git a/examples/datavisualization/audiolevels/audiolevels.cpp b/examples/datavisualization/audiolevels/audiolevels.cpp index 672e4984..c4a6b78c 100644 --- a/examples/datavisualization/audiolevels/audiolevels.cpp +++ b/examples/datavisualization/audiolevels/audiolevels.cpp @@ -74,7 +74,7 @@ AudioLevels::AudioLevels(Q3DBars *graph, QObject *parent) m_audioInput = new QAudioInput(inputDevice, formatAudio, this); #ifdef Q_OS_MAC - // Mac seems to wait for entire buffer to fill before calling writeData, so use smaller buffer + // OS X seems to wait for entire buffer to fill before calling writeData, so use smaller buffer m_audioInput->setBufferSize(256); #else m_audioInput->setBufferSize(1024); diff --git a/src/datavisualization/datavisualization.pro b/src/datavisualization/datavisualization.pro index 692965b1..f0e56073 100644 --- a/src/datavisualization/datavisualization.pro +++ b/src/datavisualization/datavisualization.pro @@ -1,5 +1,5 @@ # Target can't start with 'Qt' as it gets major version number inserted into it in that case, -# which we don't want. Exception is mac bundles, where the target name must match the module name +# which we don't want. Exception is OS X bundles, where the target name must match the module name mac:CONFIG(shared, static|shared):contains(QT_CONFIG, qt_framework) { TARGET = QtDataVisualization } else { diff --git a/src/datavisualization/doc/src/qtdatavisualization.qdoc b/src/datavisualization/doc/src/qtdatavisualization.qdoc index d3fb4856..ad53ded3 100644 --- a/src/datavisualization/doc/src/qtdatavisualization.qdoc +++ b/src/datavisualization/doc/src/qtdatavisualization.qdoc @@ -98,7 +98,7 @@ \row \li Windows (MSVC) \li nmake \row - \li OSX \li make + \li OS X \li make \endtable The above generates the default makefiles for your configuration, which is typically @@ -127,7 +127,7 @@ make release \endcode - For both builds (Windows/Mac only): + For both builds (Windows/OS X only): \code qmake CONFIG+="debug_and_release build_all" make -- cgit v1.2.3 From 2cae0e3086ee326a61d88ab4b5a1b013a9bc6c60 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Mon, 6 Oct 2014 12:09:22 +0300 Subject: Fix crash when removing and changing items for same frame MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I4fadd0cc741bf2b633d248b304c45b109a40eedc Reviewed-by: Tomi Korpipää --- src/datavisualization/engine/scatter3drenderer.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/datavisualization/engine/scatter3drenderer.cpp b/src/datavisualization/engine/scatter3drenderer.cpp index 19ead4f7..62496538 100644 --- a/src/datavisualization/engine/scatter3drenderer.cpp +++ b/src/datavisualization/engine/scatter3drenderer.cpp @@ -365,6 +365,8 @@ void Scatter3DRenderer::updateItems(const QVectorisVisible()) { const int index = item.index; + if (index >= cache->renderArray().size()) + continue; // Items removed from array for same render bool oldVisibility; ScatterRenderItem &item = cache->renderArray()[index]; if (optimizationStatic) -- cgit v1.2.3 From aa1e0a1bf46fd92c0f1173c13d33bbf00d14cb58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomi=20Korpipa=CC=88a=CC=88?= Date: Mon, 6 Oct 2014 12:24:17 +0300 Subject: Clang warning fixes Change-Id: Ic51682f7e3465c75d505e582f212eaa2cdc28c8b Reviewed-by: Miikka Heikkinen --- src/datavisualization/utils/surfaceobject.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/datavisualization/utils/surfaceobject.cpp b/src/datavisualization/utils/surfaceobject.cpp index c91075e5..8f75d666 100644 --- a/src/datavisualization/utils/surfaceobject.cpp +++ b/src/datavisualization/utils/surfaceobject.cpp @@ -179,7 +179,7 @@ void SurfaceObject::createSmoothNormalBodyLine(int &totalIndex, int column) void SurfaceObject::createSmoothNormalUpperLine(int &totalIndex) { - if ((m_dataDimension == BothAscending) ) { + if (m_dataDimension == BothAscending) { int lineEnd = m_rows * m_columns - 1; for (int j = (m_rows - 1) * m_columns; j < lineEnd; j++) { m_normals[totalIndex++] = normal(m_vertices.at(j), @@ -263,7 +263,7 @@ QVector3D SurfaceObject::createSmoothNormalBodyLineItem(int x, int y) QVector3D SurfaceObject::createSmoothNormalUpperLineItem(int x, int y) { int p = y * m_columns + x; - if ((m_dataDimension == BothAscending) ) { + if (m_dataDimension == BothAscending) { if (x < m_columns - 1) { return normal(m_vertices.at(p), m_vertices.at(p - m_columns), m_vertices.at(p + 1)); -- cgit v1.2.3 From ec4ed5f383fc0d786ad87b45702cdad50cc4e7f5 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Mon, 6 Oct 2014 12:33:33 +0300 Subject: Fix invalid pixel in water layer corner in qmlsurfacelayers example Change-Id: Ic1a32e2b23133ce8caa042ddfbf9566cd89d58f1 Reviewed-by: Miikka Heikkinen --- .../datavisualization/qmlsurfacelayers/layer_2.png | Bin 10553 -> 10563 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/examples/datavisualization/qmlsurfacelayers/layer_2.png b/examples/datavisualization/qmlsurfacelayers/layer_2.png index 61631ae8..3af154e2 100644 Binary files a/examples/datavisualization/qmlsurfacelayers/layer_2.png and b/examples/datavisualization/qmlsurfacelayers/layer_2.png differ -- cgit v1.2.3 From f1325a1e05cc7ca24d065b4b2736286df73a3d52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomi=20Korpip=C3=A4=C3=A4?= Date: Mon, 6 Oct 2014 12:40:40 +0300 Subject: Added snapshots to examples missing them MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I64a6067102f205094d7be55a9eb348b3280a7e77 Change-Id: I64a6067102f205094d7be55a9eb348b3280a7e77 Reviewed-by: Tomi Korpipää --- .../doc/images/qmlspectrogram-example.png | Bin 0 -> 80103 bytes .../volumetric/doc/images/volumetric-example.png | Bin 0 -> 177203 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 examples/datavisualization/qmlspectrogram/doc/images/qmlspectrogram-example.png create mode 100644 examples/datavisualization/volumetric/doc/images/volumetric-example.png diff --git a/examples/datavisualization/qmlspectrogram/doc/images/qmlspectrogram-example.png b/examples/datavisualization/qmlspectrogram/doc/images/qmlspectrogram-example.png new file mode 100644 index 00000000..de376cd9 Binary files /dev/null and b/examples/datavisualization/qmlspectrogram/doc/images/qmlspectrogram-example.png differ diff --git a/examples/datavisualization/volumetric/doc/images/volumetric-example.png b/examples/datavisualization/volumetric/doc/images/volumetric-example.png new file mode 100644 index 00000000..277d4fe4 Binary files /dev/null and b/examples/datavisualization/volumetric/doc/images/volumetric-example.png differ -- cgit v1.2.3 From 0e5b7fba379fe4e751bb2c609bb03d0c9bb01eaa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomi=20Korpip=C3=A4=C3=A4?= Date: Tue, 7 Oct 2014 10:45:49 +0300 Subject: Fixed shadow issue with reflections and no background Task-number: QTRD-3357 Change-Id: I7ae4460a0666316a6f455f8842d7453513f55705 Change-Id: I7ae4460a0666316a6f455f8842d7453513f55705 Reviewed-by: Miikka Heikkinen --- src/datavisualization/engine/bars3drenderer.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/datavisualization/engine/bars3drenderer.cpp b/src/datavisualization/engine/bars3drenderer.cpp index 47c9b84c..0784621e 100644 --- a/src/datavisualization/engine/bars3drenderer.cpp +++ b/src/datavisualization/engine/bars3drenderer.cpp @@ -1090,8 +1090,9 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) shadowOffset = -0.015f; } - if (m_reflectionEnabled && ((m_yFlipped && item.height() > 0.0) - || (!m_yFlipped && item.height() < 0.0))) { + if (m_cachedTheme->isBackgroundEnabled() && m_reflectionEnabled + && ((m_yFlipped && item.height() > 0.0) + || (!m_yFlipped && item.height() < 0.0))) { continue; } -- cgit v1.2.3 From aab6b4f77d408c523bd9335ffa48bcc552c00c62 Mon Sep 17 00:00:00 2001 From: Mika Salmela Date: Tue, 7 Oct 2014 00:04:03 +0300 Subject: Texture UVs for surface to follow data dimension MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change texture UVs to be generated according to data dimension. Also utilise this change on texturesurface example. Change-Id: Ideacfba409dc2e7cf579fb38d897e08c9f9a1b71 Reviewed-by: Tomi Korpipää Reviewed-by: Miikka Heikkinen --- .../datavisualization/texturesurface/topographicseries.cpp | 5 +++-- src/datavisualization/utils/surfaceobject.cpp | 12 ++++++++++++ src/datavisualization/utils/surfaceobject_p.h | 7 ++++--- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/examples/datavisualization/texturesurface/topographicseries.cpp b/examples/datavisualization/texturesurface/topographicseries.cpp index 2fa29d35..530e56b4 100644 --- a/examples/datavisualization/texturesurface/topographicseries.cpp +++ b/examples/datavisualization/texturesurface/topographicseries.cpp @@ -49,7 +49,8 @@ void TopographicSeries::setTopographyFile(const QString file, float width, float QSurfaceDataArray *dataArray = new QSurfaceDataArray; dataArray->reserve(imageHeight); for (int i = 0; i < imageHeight; i++) { - int p = (imageHeight - 1 - i) * widthBits; + int p = i * widthBits; + float z = height - float(i) * stepZ; QSurfaceDataRow *newRow = new QSurfaceDataRow(imageWidth); for (int j = 0; j < imageWidth; j++) { uchar aa = bits[p + 0]; @@ -57,7 +58,7 @@ void TopographicSeries::setTopographyFile(const QString file, float width, float uchar gg = bits[p + 2]; uint color = uint((gg << 16) + (rr << 8) + aa); float y = float(color) / packingFactor; - (*newRow)[j].setPosition(QVector3D(float(j) * stepX, y, float(i) * stepZ)); + (*newRow)[j].setPosition(QVector3D(float(j) * stepX, y, z)); p = p + 4; } *dataArray << newRow; diff --git a/src/datavisualization/utils/surfaceobject.cpp b/src/datavisualization/utils/surfaceobject.cpp index 8f75d666..86cd2789 100644 --- a/src/datavisualization/utils/surfaceobject.cpp +++ b/src/datavisualization/utils/surfaceobject.cpp @@ -307,15 +307,21 @@ void SurfaceObject::smoothUVs(const QSurfaceDataArray &dataArray, float zRangeNormalizer = dataArray.at(rows - 1)->at(0).z() - dataArray.at(0)->at(0).z(); float xMin = dataArray.at(0)->at(0).x(); float zMin = dataArray.at(0)->at(0).z(); + const bool zDescending = m_dataDimension.testFlag(SurfaceObject::ZDescending); + const bool xDescending = m_dataDimension.testFlag(SurfaceObject::XDescending); QVector uvs; uvs.resize(m_rows * m_columns); int index = 0; for (int i = 0; i < m_rows; i++) { float y = (modelArray.at(i)->at(0).z() - zMin) / zRangeNormalizer; + if (zDescending) + y = 1.0f - y; const QSurfaceDataRow &p = *modelArray.at(i); for (int j = 0; j < m_columns; j++) { float x = (p.at(j).x() - xMin) / xRangeNormalizer; + if (xDescending) + x = 1.0f - x; uvs[index] = QVector2D(x, y); index++; } @@ -601,6 +607,8 @@ void SurfaceObject::coarseUVs(const QSurfaceDataArray &dataArray, float zRangeNormalizer = dataArray.at(rows - 1)->at(0).z() - dataArray.at(0)->at(0).z(); float xMin = dataArray.at(0)->at(0).x(); float zMin = dataArray.at(0)->at(0).z(); + const bool zDescending = m_dataDimension.testFlag(SurfaceObject::ZDescending); + const bool xDescending = m_dataDimension.testFlag(SurfaceObject::XDescending); QVector uvs; uvs.resize(m_rows * m_columns * 2); @@ -608,9 +616,13 @@ void SurfaceObject::coarseUVs(const QSurfaceDataArray &dataArray, int colLimit = m_columns - 1; for (int i = 0; i < m_rows; i++) { float y = (modelArray.at(i)->at(0).z() - zMin) / zRangeNormalizer; + if (zDescending) + y = 1.0f - y; const QSurfaceDataRow &p = *modelArray.at(i); for (int j = 0; j < m_columns; j++) { float x = (p.at(j).x() - xMin) / xRangeNormalizer; + if (xDescending) + x = 1.0f - x; uvs[index] = QVector2D(x, y); index++; if (j > 0 && j < colLimit) { diff --git a/src/datavisualization/utils/surfaceobject_p.h b/src/datavisualization/utils/surfaceobject_p.h index 39d10445..e7b61310 100644 --- a/src/datavisualization/utils/surfaceobject_p.h +++ b/src/datavisualization/utils/surfaceobject_p.h @@ -49,12 +49,13 @@ public: Undefined }; - enum DataDimensions { + enum DataDimension { BothAscending = 0, XDescending = 1, ZDescending = 2, BothDescending = XDescending | ZDescending }; + Q_DECLARE_FLAGS(DataDimensions, DataDimension) public: SurfaceObject(Surface3DRenderer *renderer); @@ -115,8 +116,8 @@ private: float m_maxY; GLuint m_uvTextureBuffer; bool m_returnTextureBuffer; - int m_dataDimension; - int m_oldDataDimension; + SurfaceObject::DataDimensions m_dataDimension; + SurfaceObject::DataDimensions m_oldDataDimension; }; QT_END_NAMESPACE_DATAVISUALIZATION -- cgit v1.2.3 From 0d102fc3030e3c96144261a57a5e03c050cdf97c Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Tue, 7 Oct 2014 15:03:35 +0300 Subject: Fix minor issues found in testing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: If40f10043951409b3d6597776b60d7eb55c73eec Reviewed-by: Mika Salmela Reviewed-by: Tomi Korpipää --- examples/datavisualization/volumetric/volumetric.cpp | 2 +- src/datavisualization/utils/texturehelper.cpp | 3 +++ tests/barstest/main.cpp | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/examples/datavisualization/volumetric/volumetric.cpp b/examples/datavisualization/volumetric/volumetric.cpp index ade74fb2..788ada7a 100644 --- a/examples/datavisualization/volumetric/volumetric.cpp +++ b/examples/datavisualization/volumetric/volumetric.cpp @@ -42,7 +42,7 @@ const int mineShaftDiameter(1); const int airColorIndex(254); const int mineShaftColorIndex(255); const int layerColorThickness(60); -const int heightToColorDiv(128); +const int heightToColorDiv(140); const int magmaColorsMin(0); const int magmaColorsMax(layerColorThickness); const int aboveWaterGroundColorsMin(magmaColorsMax + 1); diff --git a/src/datavisualization/utils/texturehelper.cpp b/src/datavisualization/utils/texturehelper.cpp index 9dbf0613..1d8de71d 100644 --- a/src/datavisualization/utils/texturehelper.cpp +++ b/src/datavisualization/utils/texturehelper.cpp @@ -45,6 +45,9 @@ TextureHelper::TextureHelper() TextureHelper::~TextureHelper() { +#if !defined(QT_OPENGL_ES_2) + delete m_openGlFunctions_2_1; +#endif } GLuint TextureHelper::create2DTexture(const QImage &image, bool useTrilinearFiltering, diff --git a/tests/barstest/main.cpp b/tests/barstest/main.cpp index a468cb57..9a791c5b 100644 --- a/tests/barstest/main.cpp +++ b/tests/barstest/main.cpp @@ -236,7 +236,7 @@ int main(int argc, char **argv) QCheckBox *inputHandlerZoomAtTargetCheckBox = new QCheckBox(widget); inputHandlerZoomAtTargetCheckBox->setText("IH: setZoomAtTarget"); - inputHandlerZoomAtTargetCheckBox->setChecked(false); + inputHandlerZoomAtTargetCheckBox->setChecked(true); QSlider *rotationSliderX = new QSlider(Qt::Horizontal, widget); rotationSliderX->setTickInterval(1); -- cgit v1.2.3 From 5308c49eeb4ad15005dfa1c8e9d38d96298fbb07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomi=20Korpip=C3=A4=C3=A4?= Date: Wed, 8 Oct 2014 09:06:46 +0300 Subject: Polar coordinates angle grid lines fixes Task-number: QTRD-3360 Change-Id: I33177ccfe93136e6ce048fad0dbed70fe560b204 Change-Id: I33177ccfe93136e6ce048fad0dbed70fe560b204 Reviewed-by: Miikka Heikkinen --- src/datavisualization/engine/abstract3drenderer.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/datavisualization/engine/abstract3drenderer.cpp b/src/datavisualization/engine/abstract3drenderer.cpp index 0cbf2e26..a5b517fd 100644 --- a/src/datavisualization/engine/abstract3drenderer.cpp +++ b/src/datavisualization/engine/abstract3drenderer.cpp @@ -1858,7 +1858,11 @@ void Abstract3DRenderer::drawAngularGrid(ShaderHelper *shader, float yFloorLineP const QVector &subGridPositions = m_axisCacheX.formatter()->subGridPositions(); int mainSize = gridPositions.size(); QVector3D translateVector(0.0f, yFloorLinePos, -halfRatio); +#if defined(QT_OPENGL_ES_2) + QQuaternion finalRotation = m_yRightAngleRotationNeg; +#else QQuaternion finalRotation = m_xRightAngleRotationNeg; +#endif if (m_yFlippedForGrid) finalRotation *= m_xFlipRotation; for (int i = 0; i < gridLineCount; i++) { -- cgit v1.2.3 From f644893ceaf6628955bc44b127c6f7c720501b2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomi=20Korpip=C3=A4=C3=A4?= Date: Wed, 8 Oct 2014 10:01:53 +0300 Subject: Grid line color bug in surface fixed Task-number: QTRD-3361 Change-Id: Id40db146efb84f6a9b642e7e0678d6b656cdbd19 Change-Id: Id40db146efb84f6a9b642e7e0678d6b656cdbd19 Reviewed-by: Miikka Heikkinen --- src/datavisualization/engine/surface3drenderer.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/datavisualization/engine/surface3drenderer.cpp b/src/datavisualization/engine/surface3drenderer.cpp index 02add48f..df78d491 100644 --- a/src/datavisualization/engine/surface3drenderer.cpp +++ b/src/datavisualization/engine/surface3drenderer.cpp @@ -929,7 +929,7 @@ void Surface3DRenderer::drawSlicedScene() #if !(defined QT_OPENGL_ES_2) ShaderHelper *lineShader = m_backgroundShader; #else - ShaderHelper *lineShader = m_selectionShader; // Plain color shader for GL_LINES + ShaderHelper *lineShader = m_surfaceGridShader; // Plain color shader for GL_LINES #endif // Bind line shader @@ -1571,7 +1571,7 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) #if !(defined QT_OPENGL_ES_2) ShaderHelper *lineShader = m_backgroundShader; #else - ShaderHelper *lineShader = m_selectionShader; // Plain color shader for GL_LINES + ShaderHelper *lineShader = m_surfaceGridShader; // Plain color shader for GL_LINES #endif // Bind line shader -- cgit v1.2.3 From a350868f0e49b52b5bb35ba5e320a0d7dc3f698a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomi=20Korpip=C3=A4=C3=A4?= Date: Wed, 8 Oct 2014 11:16:09 +0300 Subject: Fixed selecting through the floor bug Task-number: QTRD-3359 Change-Id: I418aaef640c05f7af7b860c89f5a5231e3f14c16 Reviewed-by: Miikka Heikkinen --- src/datavisualization/engine/bars3drenderer.cpp | 79 ++++++++++++++----------- src/datavisualization/engine/bars3drenderer_p.h | 2 +- 2 files changed, 44 insertions(+), 37 deletions(-) diff --git a/src/datavisualization/engine/bars3drenderer.cpp b/src/datavisualization/engine/bars3drenderer.cpp index 0784621e..7e2de98f 100644 --- a/src/datavisualization/engine/bars3drenderer.cpp +++ b/src/datavisualization/engine/bars3drenderer.cpp @@ -1242,11 +1242,12 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) projectionViewMatrix, depthProjectionViewMatrix, m_depthTexture, m_shadowQualityToShader); drawLabels(true, activeCamera, viewMatrix, projectionMatrix); + drawBackground(backgroundRotation, depthProjectionViewMatrix, projectionViewMatrix, + viewMatrix, false, true); glEnable(GL_DITHER); // Read color under cursor - QVector4D clickedColor = Utils::getSelection(m_inputPosition, - m_viewport.height()); + QVector4D clickedColor = Utils::getSelection(m_inputPosition, m_viewport.height()); m_clickedPosition = selectionColorToArrayPosition(clickedColor); m_clickedSeries = selectionColorToSeries(clickedColor); @@ -1688,17 +1689,23 @@ bool Bars3DRenderer::drawBars(BarRenderItem **selectedBar, void Bars3DRenderer::drawBackground(GLfloat backgroundRotation, const QMatrix4x4 &depthProjectionViewMatrix, const QMatrix4x4 &projectionViewMatrix, - const QMatrix4x4 &viewMatrix, bool reflectingDraw) + const QMatrix4x4 &viewMatrix, bool reflectingDraw, + bool drawingSelectionBuffer) { - QVector3D lightPos = m_cachedScene->activeLight()->position(); - QVector4D lightColor = Utils::vectorFromColor(m_cachedTheme->lightColor()); - GLfloat adjustedLightStrength = m_cachedTheme->lightStrength() / 10.0f; - - // Bind background shader - m_backgroundShader->bind(); - // Draw background if (m_cachedTheme->isBackgroundEnabled() && m_backgroundObj) { + QVector3D lightPos = m_cachedScene->activeLight()->position(); + QVector4D lightColor = Utils::vectorFromColor(m_cachedTheme->lightColor()); + GLfloat adjustedLightStrength = m_cachedTheme->lightStrength() / 10.0f; + ShaderHelper *shader = 0; + + // Bind background shader + if (drawingSelectionBuffer) + shader = m_selectionShader; // Use single color shader when drawing to selection buffer + else + shader = m_backgroundShader; + shader->bind(); + QMatrix4x4 modelMatrix; QMatrix4x4 MVPMatrix; QMatrix4x4 itModelMatrix; @@ -1710,12 +1717,17 @@ void Bars3DRenderer::drawBackground(GLfloat backgroundRotation, backgroundColor.setW(backgroundColor.w() * m_reflectivity); // Set shader bindings - m_backgroundShader->setUniformValue(m_backgroundShader->lightP(), lightPos); - m_backgroundShader->setUniformValue(m_backgroundShader->view(), viewMatrix); - m_backgroundShader->setUniformValue(m_backgroundShader->color(), backgroundColor); - m_backgroundShader->setUniformValue(m_backgroundShader->ambientS(), - m_cachedTheme->ambientLightStrength() * 2.0f); - m_backgroundShader->setUniformValue(m_backgroundShader->lightColor(), lightColor); + shader->setUniformValue(shader->lightP(), lightPos); + shader->setUniformValue(shader->view(), viewMatrix); + if (drawingSelectionBuffer) { + // Use selectionSkipColor for background when drawing to selection buffer + shader->setUniformValue(shader->color(), selectionSkipColor); + } else { + shader->setUniformValue(shader->color(), backgroundColor); + } + shader->setUniformValue(shader->ambientS(), + m_cachedTheme->ambientLightStrength() * 2.0f); + shader->setUniformValue(shader->lightColor(), lightColor); // Draw floor modelMatrix.scale(backgroundScaler); @@ -1733,23 +1745,22 @@ void Bars3DRenderer::drawBackground(GLfloat backgroundRotation, MVPMatrix = projectionViewMatrix * modelMatrix; #endif // Set changed shader bindings - m_backgroundShader->setUniformValue(m_backgroundShader->model(), modelMatrix); - m_backgroundShader->setUniformValue(m_backgroundShader->nModel(), - itModelMatrix.inverted().transposed()); - m_backgroundShader->setUniformValue(m_backgroundShader->MVP(), MVPMatrix); + shader->setUniformValue(shader->model(), modelMatrix); + shader->setUniformValue(shader->nModel(), itModelMatrix.inverted().transposed()); + shader->setUniformValue(shader->MVP(), MVPMatrix); #if !defined(QT_OPENGL_ES_2) if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { // Set shadow shader bindings QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix; - m_backgroundShader->setUniformValue(m_backgroundShader->depth(), depthMVPMatrix); + shader->setUniformValue(shader->depth(), depthMVPMatrix); // Draw the object - m_drawer->drawObject(m_backgroundShader, m_gridLineObj, 0, m_depthTexture); + m_drawer->drawObject(shader, m_gridLineObj, 0, m_depthTexture); } else #endif { // Draw the object - m_drawer->drawObject(m_backgroundShader, m_gridLineObj); + m_drawer->drawObject(shader, m_gridLineObj); } // Draw walls @@ -1769,23 +1780,20 @@ void Bars3DRenderer::drawBackground(GLfloat backgroundRotation, #endif // Set changed shader bindings - m_backgroundShader->setUniformValue(m_backgroundShader->model(), modelMatrix); - m_backgroundShader->setUniformValue(m_backgroundShader->nModel(), - itModelMatrix.inverted().transposed()); - m_backgroundShader->setUniformValue(m_backgroundShader->MVP(), MVPMatrix); + shader->setUniformValue(shader->model(), modelMatrix); + shader->setUniformValue(shader->nModel(), itModelMatrix.inverted().transposed()); + shader->setUniformValue(shader->MVP(), MVPMatrix); if (!m_reflectionEnabled || (m_reflectionEnabled && reflectingDraw)) { #if !defined(QT_OPENGL_ES_2) if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { // Set shadow shader bindings QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix; - m_backgroundShader->setUniformValue(m_backgroundShader->shadowQ(), - m_shadowQualityToShader); - m_backgroundShader->setUniformValue(m_backgroundShader->depth(), depthMVPMatrix); - m_backgroundShader->setUniformValue(m_backgroundShader->lightS(), - adjustedLightStrength); + shader->setUniformValue(shader->shadowQ(), m_shadowQualityToShader); + shader->setUniformValue(shader->depth(), depthMVPMatrix); + shader->setUniformValue(shader->lightS(), adjustedLightStrength); // Draw the object - m_drawer->drawObject(m_backgroundShader, m_backgroundObj, 0, m_depthTexture); + m_drawer->drawObject(shader, m_backgroundObj, 0, m_depthTexture); } else #else Q_UNUSED(adjustedLightStrength); @@ -1793,11 +1801,10 @@ void Bars3DRenderer::drawBackground(GLfloat backgroundRotation, #endif { // Set shadowless shader bindings - m_backgroundShader->setUniformValue(m_backgroundShader->lightS(), - m_cachedTheme->lightStrength()); + shader->setUniformValue(shader->lightS(), m_cachedTheme->lightStrength()); // Draw the object - m_drawer->drawObject(m_backgroundShader, m_backgroundObj); + m_drawer->drawObject(shader, m_backgroundObj); } } } diff --git a/src/datavisualization/engine/bars3drenderer_p.h b/src/datavisualization/engine/bars3drenderer_p.h index fea4e52e..726bbffe 100644 --- a/src/datavisualization/engine/bars3drenderer_p.h +++ b/src/datavisualization/engine/bars3drenderer_p.h @@ -159,7 +159,7 @@ private: GLint startBar, GLint stopBar, GLint stepBar, GLfloat reflection = 1.0f); void drawBackground(GLfloat backgroundRotation, const QMatrix4x4 &depthProjectionViewMatrix, const QMatrix4x4 &projectionViewMatrix, const QMatrix4x4 &viewMatrix, - bool reflectingDraw = false); + bool reflectingDraw = false, bool drawingSelectionBuffer = false); void drawGridLines(const QMatrix4x4 &depthProjectionViewMatrix, const QMatrix4x4 &projectionViewMatrix, const QMatrix4x4 &viewMatrix); -- cgit v1.2.3 From f3a73782738c5868541086eadd5e5c7b4aaabc14 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Wed, 8 Oct 2014 15:35:15 +0300 Subject: Fix screen position based queries in threaded rendering environment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit QtQuick's threaded renderer doesn't seem to always pair up sync and render (or more accurately, beforeSynchronizing and node's preprocess calls). Sometimes sync comes without a followup render, or there are two syncs in a row, even though the sync is using direct connection. Both of these cases broke the old logic for handling position based queries. Changed the logic to actually ensure we have resolved the query before emitting the relevant signal. Task-number: QTRD-3358 Change-Id: Ica6c8c311f53a06311c21532aaabc18c28556655 Reviewed-by: Tomi Korpipää --- .../engine/abstract3dcontroller.cpp | 27 ++++++++++++++------- .../engine/abstract3drenderer.cpp | 19 ++++++++------- .../engine/abstract3drenderer_p.h | 13 ++++++---- src/datavisualization/engine/bars3drenderer.cpp | 1 + src/datavisualization/engine/q3dscene.cpp | 5 ++++ src/datavisualization/engine/scatter3drenderer.cpp | 1 + src/datavisualization/engine/surface3drenderer.cpp | 1 + tests/qmldynamicdata/qml/qmldynamicdata/main.qml | 28 ++++++++++++---------- 8 files changed, 62 insertions(+), 33 deletions(-) diff --git a/src/datavisualization/engine/abstract3dcontroller.cpp b/src/datavisualization/engine/abstract3dcontroller.cpp index 27d01029..f8a1a813 100644 --- a/src/datavisualization/engine/abstract3dcontroller.cpp +++ b/src/datavisualization/engine/abstract3dcontroller.cpp @@ -170,16 +170,14 @@ void Abstract3DController::synchDataToRenderer() { // Subclass implementations check for renderer validity already, so no need to check here. + m_renderPending = false; + // If there are pending queries, handle those first - if (m_renderer->isGraphPositionQueryPending()) { + if (m_renderer->isGraphPositionQueryResolved()) handlePendingGraphPositionQuery(); - m_renderer->clearGraphPositionQueryPending(); - } - if (m_renderer->isClickPending()) { + if (m_renderer->isClickQueryResolved()) handlePendingClick(); - m_renderer->clearClickPending(); - } startRecordingRemovesAndInserts(); @@ -518,8 +516,6 @@ void Abstract3DController::synchDataToRenderer() void Abstract3DController::render(const GLuint defaultFboHandle) { - m_renderPending = false; - // If not initialized, do nothing. if (!m_renderer) return; @@ -1489,12 +1485,27 @@ void Abstract3DController::handlePendingClick() m_selectedLabelIndex = m_renderer->m_selectedLabelIndex; m_selectedCustomItemIndex = m_renderer->m_selectedCustomItemIndex; + // Invalidate query position to indicate the query has been handled, unless another + // point has been queried. + if (m_renderer->cachedClickQuery() == m_scene->selectionQueryPosition()) + m_scene->setSelectionQueryPosition(Q3DScene::invalidSelectionPoint()); + + m_renderer->clearClickQueryResolved(); + emit elementSelected(m_clickedType); } void Abstract3DController::handlePendingGraphPositionQuery() { m_queriedGraphPosition = m_renderer->queriedGraphPosition(); + + // Invalidate query position to indicate the query has been handled, unless another + // point has been queried. + if (m_renderer->cachedGraphPositionQuery() == m_scene->graphPositionQuery()) + m_scene->setGraphPositionQuery(Q3DScene::invalidSelectionPoint()); + + m_renderer->clearGraphPositionQueryResolved(); + emit queriedGraphPositionChanged(m_queriedGraphPosition); } diff --git a/src/datavisualization/engine/abstract3drenderer.cpp b/src/datavisualization/engine/abstract3drenderer.cpp index a5b517fd..c47e2b29 100644 --- a/src/datavisualization/engine/abstract3drenderer.cpp +++ b/src/datavisualization/engine/abstract3drenderer.cpp @@ -53,8 +53,9 @@ Abstract3DRenderer::Abstract3DRenderer(Abstract3DController *controller) m_selectionState(SelectNone), m_devicePixelRatio(1.0f), m_selectionLabelDirty(true), - m_clickPending(false), + m_clickResolved(false), m_graphPositionQueryPending(false), + m_graphPositionQueryResolved(false), m_clickedSeries(0), m_clickedType(QAbstract3DGraph::ElementNone), m_selectedLabelIndex(-1), @@ -337,10 +338,6 @@ void Abstract3DRenderer::updateScene(Q3DScene *scene) if (Q3DScene::invalidSelectionPoint() == logicalPixelPosition) { updateSelectionState(SelectNone); } else { - // Selections are one-shot, reset selection active to false before processing - scene->setSelectionQueryPosition(Q3DScene::invalidSelectionPoint()); - m_clickPending = true; - if (scene->isSlicingActive()) { if (scene->isPointInPrimarySubView(logicalPixelPosition)) updateSelectionState(SelectOnOverview); @@ -353,10 +350,14 @@ void Abstract3DRenderer::updateScene(Q3DScene *scene) } } - if (Q3DScene::invalidSelectionPoint() != logicalGraphPosition) { + if (Q3DScene::invalidSelectionPoint() != logicalGraphPosition) m_graphPositionQueryPending = true; - scene->setGraphPositionQuery(Q3DScene::invalidSelectionPoint()); - } + + // Queue up another render when we have a query that needs resolving. + // This is needed because QtQuick scene graph can sometimes do a sync without following it up + // with a render. + if (m_graphPositionQueryPending || m_selectionState != SelectNone) + emit needRender(); } void Abstract3DRenderer::updateTextures() @@ -1767,6 +1768,8 @@ void Abstract3DRenderer::queriedGraphPosition(const QMatrix4x4 &projectionViewMa m_queriedGraphPosition = QVector3D(normalizedValues.x(), normalizedValues.y(), normalizedValues.z()); + m_graphPositionQueryResolved = true; + m_graphPositionQueryPending = false; } void Abstract3DRenderer::calculatePolarXZ(const QVector3D &dataPos, float &x, float &z) const diff --git a/src/datavisualization/engine/abstract3drenderer_p.h b/src/datavisualization/engine/abstract3drenderer_p.h index 4833afaa..d8ca7696 100644 --- a/src/datavisualization/engine/abstract3drenderer_p.h +++ b/src/datavisualization/engine/abstract3drenderer_p.h @@ -148,13 +148,15 @@ public: void generateBaseColorTexture(const QColor &color, GLuint *texture); void fixGradientAndGenerateTexture(QLinearGradient *gradient, GLuint *gradientTexture); - inline bool isClickPending() { return m_clickPending; } - inline void clearClickPending() { m_clickPending = false; } + inline bool isClickQueryResolved() const { return m_clickResolved; } + inline void clearClickQueryResolved() { m_clickResolved = false; } + inline QPoint cachedClickQuery() const { return m_cachedScene->selectionQueryPosition(); } inline QAbstract3DSeries *clickedSeries() const { return m_clickedSeries; } inline QAbstract3DGraph::ElementType clickedType() { return m_clickedType; } - inline bool isGraphPositionQueryPending() { return m_graphPositionQueryPending; } - inline void clearGraphPositionQueryPending() { m_graphPositionQueryPending = false; } + inline bool isGraphPositionQueryResolved() const { return m_graphPositionQueryResolved; } + inline void clearGraphPositionQueryResolved() { m_graphPositionQueryResolved = false; } inline QVector3D queriedGraphPosition() const { return m_queriedGraphPosition; } + inline QPoint cachedGraphPositionQuery() const { return m_cachedScene->graphPositionQuery(); } LabelItem &selectionLabelItem(); void setSelectionLabel(const QString &label); @@ -253,8 +255,9 @@ protected: QRect m_secondarySubViewport; float m_devicePixelRatio; bool m_selectionLabelDirty; - bool m_clickPending; + bool m_clickResolved; bool m_graphPositionQueryPending; + bool m_graphPositionQueryResolved; QAbstract3DSeries *m_clickedSeries; QAbstract3DGraph::ElementType m_clickedType; int m_selectedLabelIndex; diff --git a/src/datavisualization/engine/bars3drenderer.cpp b/src/datavisualization/engine/bars3drenderer.cpp index 7e2de98f..e18e4fa5 100644 --- a/src/datavisualization/engine/bars3drenderer.cpp +++ b/src/datavisualization/engine/bars3drenderer.cpp @@ -1250,6 +1250,7 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) QVector4D clickedColor = Utils::getSelection(m_inputPosition, m_viewport.height()); m_clickedPosition = selectionColorToArrayPosition(clickedColor); m_clickedSeries = selectionColorToSeries(clickedColor); + m_clickResolved = true; emit needRender(); diff --git a/src/datavisualization/engine/q3dscene.cpp b/src/datavisualization/engine/q3dscene.cpp index 6ea41d6a..e4015c2b 100644 --- a/src/datavisualization/engine/q3dscene.cpp +++ b/src/datavisualization/engine/q3dscene.cpp @@ -560,6 +560,11 @@ void Q3DScenePrivate::sync(Q3DScenePrivate &other) m_changeTracker.selectionQueryPositionChanged = false; other.m_changeTracker.selectionQueryPositionChanged = false; } + if (m_changeTracker.graphPositionQueryPositionChanged) { + other.q_ptr->setGraphPositionQuery(q_ptr->graphPositionQuery()); + m_changeTracker.graphPositionQueryPositionChanged = false; + other.m_changeTracker.graphPositionQueryPositionChanged = false; + } if (m_changeTracker.cameraChanged) { m_camera->setDirty(true); m_changeTracker.cameraChanged = false; diff --git a/src/datavisualization/engine/scatter3drenderer.cpp b/src/datavisualization/engine/scatter3drenderer.cpp index 62496538..f5d0793f 100644 --- a/src/datavisualization/engine/scatter3drenderer.cpp +++ b/src/datavisualization/engine/scatter3drenderer.cpp @@ -802,6 +802,7 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) QVector4D clickedColor = Utils::getSelection(m_inputPosition, m_viewport.height()); selectionColorToSeriesAndIndex(clickedColor, m_clickedIndex, m_clickedSeries); + m_clickResolved = true; emit needRender(); diff --git a/src/datavisualization/engine/surface3drenderer.cpp b/src/datavisualization/engine/surface3drenderer.cpp index df78d491..15df5598 100644 --- a/src/datavisualization/engine/surface3drenderer.cpp +++ b/src/datavisualization/engine/surface3drenderer.cpp @@ -1353,6 +1353,7 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) + uint(clickedColor.w()) * alphaMultiplier; m_clickedPosition = selectionIdToSurfacePoint(selectionId); + m_clickResolved = true; emit needRender(); diff --git a/tests/qmldynamicdata/qml/qmldynamicdata/main.qml b/tests/qmldynamicdata/qml/qmldynamicdata/main.qml index 0ec9d277..29c51fb3 100644 --- a/tests/qmldynamicdata/qml/qmldynamicdata/main.qml +++ b/tests/qmldynamicdata/qml/qmldynamicdata/main.qml @@ -71,18 +71,16 @@ Rectangle { isIncreasing = false; } } else { - // TODO: Once QTRD-2645 is fixed, change this to remove from - // random index to add coverage. - graphModel.remove(2); - graphModel.remove(2); - graphModel.remove(2); - graphModel.remove(2); - graphModel.remove(2); - graphModel.remove(2); - graphModel.remove(2); - graphModel.remove(2); - graphModel.remove(2); - graphModel.remove(2); + graphModel.remove(Math.random() * (graphModel.count - 1)); + graphModel.remove(Math.random() * (graphModel.count - 1)); + graphModel.remove(Math.random() * (graphModel.count - 1)); + graphModel.remove(Math.random() * (graphModel.count - 1)); + graphModel.remove(Math.random() * (graphModel.count - 1)); + graphModel.remove(Math.random() * (graphModel.count - 1)); + graphModel.remove(Math.random() * (graphModel.count - 1)); + graphModel.remove(Math.random() * (graphModel.count - 1)); + graphModel.remove(Math.random() * (graphModel.count - 1)); + graphModel.remove(Math.random() * (graphModel.count - 1)); if (graphModel.count == 2) { scatterGraph.theme.type = Theme3D.ThemeDigia; isIncreasing = true; @@ -118,6 +116,12 @@ Rectangle { shadowQuality: AbstractGraph3D.ShadowQualitySoftMedium scene.activeCamera.yRotation: 30.0 inputHandler: null + axisX.min: 0 + axisY.min: 0 + axisZ.min: 0 + axisX.max: 1 + axisY.max: 1 + axisZ.max: 1 Scatter3DSeries { id: scatterSeries -- cgit v1.2.3 From d76b078947c2d440e4760384967e8bac9c7bd3f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomi=20Korpip=C3=A4=C3=A4?= Date: Thu, 9 Oct 2014 07:49:39 +0300 Subject: Fixed recurring GL_INVALID_VALUE OpenGL errors Now that these are not generated all the time, makes it easier to use glGetError to find actual errors. Change-Id: I4e10f5798e4e79466a47a0d671c04b445899c62c Change-Id: I4e10f5798e4e79466a47a0d671c04b445899c62c Reviewed-by: Miikka Heikkinen --- src/datavisualization/engine/drawer.cpp | 22 +++-- src/datavisualization/utils/shaderhelper.cpp | 104 ++++++++++++-------- src/datavisualization/utils/shaderhelper_p.h | 136 +++++++++++++-------------- 3 files changed, 149 insertions(+), 113 deletions(-) diff --git a/src/datavisualization/engine/drawer.cpp b/src/datavisualization/engine/drawer.cpp index 2cb88e5c..939d4827 100644 --- a/src/datavisualization/engine/drawer.cpp +++ b/src/datavisualization/engine/drawer.cpp @@ -126,14 +126,18 @@ void Drawer::drawObject(ShaderHelper *shader, AbstractObjectHelper *object, GLui glVertexAttribPointer(shader->posAtt(), 3, GL_FLOAT, GL_FALSE, 0, (void*)0); // 2nd attribute buffer : normals - glEnableVertexAttribArray(shader->normalAtt()); - glBindBuffer(GL_ARRAY_BUFFER, object->normalBuf()); - glVertexAttribPointer(shader->normalAtt(), 3, GL_FLOAT, GL_FALSE, 0, (void*)0); + if (shader->normalAtt() >= 0) { + glEnableVertexAttribArray(shader->normalAtt()); + glBindBuffer(GL_ARRAY_BUFFER, object->normalBuf()); + glVertexAttribPointer(shader->normalAtt(), 3, GL_FLOAT, GL_FALSE, 0, (void*)0); + } // 3rd attribute buffer : UVs - glEnableVertexAttribArray(shader->uvAtt()); - glBindBuffer(GL_ARRAY_BUFFER, object->uvBuf()); - glVertexAttribPointer(shader->uvAtt(), 2, GL_FLOAT, GL_FALSE, 0, (void*)0); + if (shader->uvAtt() >= 0) { + glEnableVertexAttribArray(shader->uvAtt()); + glBindBuffer(GL_ARRAY_BUFFER, object->uvBuf()); + glVertexAttribPointer(shader->uvAtt(), 2, GL_FLOAT, GL_FALSE, 0, (void*)0); + } // Index buffer glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, object->elementBuf()); @@ -145,8 +149,10 @@ void Drawer::drawObject(ShaderHelper *shader, AbstractObjectHelper *object, GLui glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); - glDisableVertexAttribArray(shader->uvAtt()); - glDisableVertexAttribArray(shader->normalAtt()); + if (shader->uvAtt() >= 0) + glDisableVertexAttribArray(shader->uvAtt()); + if (shader->normalAtt() >= 0) + glDisableVertexAttribArray(shader->normalAtt()); glDisableVertexAttribArray(shader->posAtt()); // Release textures diff --git a/src/datavisualization/utils/shaderhelper.cpp b/src/datavisualization/utils/shaderhelper.cpp index aa43c2df..bbd7fe0e 100644 --- a/src/datavisualization/utils/shaderhelper.cpp +++ b/src/datavisualization/utils/shaderhelper.cpp @@ -40,7 +40,37 @@ ShaderHelper::ShaderHelper(QObject *parent, m_vertexShaderFile(vertexShader), m_fragmentShaderFile(fragmentShader), m_textureFile(texture), - m_depthTextureFile(depthTexture) + m_depthTextureFile(depthTexture), + m_positionAttr(0), + m_uvAttr(0), + m_normalAttr(0), + m_colorUniform(0), + m_viewMatrixUniform(0), + m_modelMatrixUniform(0), + m_invTransModelMatrixUniform(0), + m_depthMatrixUniform(0), + m_mvpMatrixUniform(0), + m_lightPositionUniform(0), + m_lightStrengthUniform(0), + m_ambientStrengthUniform(0), + m_shadowQualityUniform(0), + m_textureUniform(0), + m_shadowUniform(0), + m_gradientMinUniform(0), + m_gradientHeightUniform(0), + m_lightColorUniform(0), + m_volumeSliceIndicesUniform(0), + m_colorIndexUniform(0), + m_cameraPositionRelativeToModelUniform(0), + m_color8BitUniform(0), + m_textureDimensionsUniform(0), + m_sampleCountUniform(0), + m_alphaMultiplierUniform(0), + m_preserveOpacityUniform(0), + m_minBoundsUniform(0), + m_maxBoundsUniform(0), + m_sliceFrameWidthUniform(0), + m_initialized(false) { } @@ -136,217 +166,217 @@ void ShaderHelper::release() m_program->release(); } -void ShaderHelper::setUniformValue(GLuint uniform, const QVector2D &value) +void ShaderHelper::setUniformValue(GLint uniform, const QVector2D &value) { m_program->setUniformValue(uniform, value); } -void ShaderHelper::setUniformValue(GLuint uniform, const QVector3D &value) +void ShaderHelper::setUniformValue(GLint uniform, const QVector3D &value) { m_program->setUniformValue(uniform, value); } -void ShaderHelper::setUniformValue(GLuint uniform, const QVector4D &value) +void ShaderHelper::setUniformValue(GLint uniform, const QVector4D &value) { m_program->setUniformValue(uniform, value); } -void ShaderHelper::setUniformValue(GLuint uniform, const QMatrix4x4 &value) +void ShaderHelper::setUniformValue(GLint uniform, const QMatrix4x4 &value) { m_program->setUniformValue(uniform, value); } -void ShaderHelper::setUniformValue(GLuint uniform, GLfloat value) +void ShaderHelper::setUniformValue(GLint uniform, GLfloat value) { m_program->setUniformValue(uniform, value); } -void ShaderHelper::setUniformValue(GLuint uniform, GLint value) +void ShaderHelper::setUniformValue(GLint uniform, GLint value) { m_program->setUniformValue(uniform, value); } -void ShaderHelper::setUniformValueArray(GLuint uniform, const QVector4D *values, int count) +void ShaderHelper::setUniformValueArray(GLint uniform, const QVector4D *values, int count) { m_program->setUniformValueArray(uniform, values, count); } -GLuint ShaderHelper::MVP() +GLint ShaderHelper::MVP() { if (!m_initialized) qFatal("Shader not initialized"); return m_mvpMatrixUniform; } -GLuint ShaderHelper::view() +GLint ShaderHelper::view() { if (!m_initialized) qFatal("Shader not initialized"); return m_viewMatrixUniform; } -GLuint ShaderHelper::model() +GLint ShaderHelper::model() { if (!m_initialized) qFatal("Shader not initialized"); return m_modelMatrixUniform; } -GLuint ShaderHelper::nModel() +GLint ShaderHelper::nModel() { if (!m_initialized) qFatal("Shader not initialized"); return m_invTransModelMatrixUniform; } -GLuint ShaderHelper::depth() +GLint ShaderHelper::depth() { if (!m_initialized) qFatal("Shader not initialized"); return m_depthMatrixUniform; } -GLuint ShaderHelper::lightP() +GLint ShaderHelper::lightP() { if (!m_initialized) qFatal("Shader not initialized"); return m_lightPositionUniform; } -GLuint ShaderHelper::lightS() +GLint ShaderHelper::lightS() { if (!m_initialized) qFatal("Shader not initialized"); return m_lightStrengthUniform; } -GLuint ShaderHelper::ambientS() +GLint ShaderHelper::ambientS() { if (!m_initialized) qFatal("Shader not initialized"); return m_ambientStrengthUniform; } -GLuint ShaderHelper::shadowQ() +GLint ShaderHelper::shadowQ() { if (!m_initialized) qFatal("Shader not initialized"); return m_shadowQualityUniform; } -GLuint ShaderHelper::color() +GLint ShaderHelper::color() { if (!m_initialized) qFatal("Shader not initialized"); return m_colorUniform; } -GLuint ShaderHelper::texture() +GLint ShaderHelper::texture() { if (!m_initialized) qFatal("Shader not initialized"); return m_textureUniform; } -GLuint ShaderHelper::shadow() +GLint ShaderHelper::shadow() { if (!m_initialized) qFatal("Shader not initialized"); return m_shadowUniform; } -GLuint ShaderHelper::gradientMin() +GLint ShaderHelper::gradientMin() { if (!m_initialized) qFatal("Shader not initialized"); return m_gradientMinUniform; } -GLuint ShaderHelper::gradientHeight() +GLint ShaderHelper::gradientHeight() { if (!m_initialized) qFatal("Shader not initialized"); return m_gradientHeightUniform; } -GLuint ShaderHelper::lightColor() +GLint ShaderHelper::lightColor() { if (!m_initialized) qFatal("Shader not initialized"); return m_lightColorUniform; } -GLuint ShaderHelper::volumeSliceIndices() +GLint ShaderHelper::volumeSliceIndices() { if (!m_initialized) qFatal("Shader not initialized"); return m_volumeSliceIndicesUniform; } -GLuint ShaderHelper::colorIndex() +GLint ShaderHelper::colorIndex() { if (!m_initialized) qFatal("Shader not initialized"); return m_colorIndexUniform; } -GLuint ShaderHelper::cameraPositionRelativeToModel() +GLint ShaderHelper::cameraPositionRelativeToModel() { if (!m_initialized) qFatal("Shader not initialized"); return m_cameraPositionRelativeToModelUniform; } -GLuint ShaderHelper::color8Bit() +GLint ShaderHelper::color8Bit() { if (!m_initialized) qFatal("Shader not initialized"); return m_color8BitUniform; } -GLuint ShaderHelper::textureDimensions() +GLint ShaderHelper::textureDimensions() { if (!m_initialized) qFatal("Shader not initialized"); return m_textureDimensionsUniform; } -GLuint ShaderHelper::sampleCount() +GLint ShaderHelper::sampleCount() { if (!m_initialized) qFatal("Shader not initialized"); return m_sampleCountUniform; } -GLuint ShaderHelper::alphaMultiplier() +GLint ShaderHelper::alphaMultiplier() { if (!m_initialized) qFatal("Shader not initialized"); return m_alphaMultiplierUniform; } -GLuint ShaderHelper::preserveOpacity() +GLint ShaderHelper::preserveOpacity() { if (!m_initialized) qFatal("Shader not initialized"); return m_preserveOpacityUniform; } -GLuint ShaderHelper::maxBounds() +GLint ShaderHelper::maxBounds() { if (!m_initialized) qFatal("Shader not initialized"); return m_maxBoundsUniform; } -GLuint ShaderHelper::minBounds() +GLint ShaderHelper::minBounds() { if (!m_initialized) qFatal("Shader not initialized"); return m_minBoundsUniform; } -GLuint ShaderHelper::sliceFrameWidth() +GLint ShaderHelper::sliceFrameWidth() { if (!m_initialized) @@ -354,21 +384,21 @@ GLuint ShaderHelper::sliceFrameWidth() return m_sliceFrameWidthUniform; } -GLuint ShaderHelper::posAtt() +GLint ShaderHelper::posAtt() { if (!m_initialized) qFatal("Shader not initialized"); return m_positionAttr; } -GLuint ShaderHelper::uvAtt() +GLint ShaderHelper::uvAtt() { if (!m_initialized) qFatal("Shader not initialized"); return m_uvAttr; } -GLuint ShaderHelper::normalAtt() +GLint ShaderHelper::normalAtt() { if (!m_initialized) qFatal("Shader not initialized"); diff --git a/src/datavisualization/utils/shaderhelper_p.h b/src/datavisualization/utils/shaderhelper_p.h index da010cbd..812cba18 100644 --- a/src/datavisualization/utils/shaderhelper_p.h +++ b/src/datavisualization/utils/shaderhelper_p.h @@ -52,44 +52,44 @@ class ShaderHelper bool testCompile(); void bind(); void release(); - void setUniformValue(GLuint uniform, const QVector2D &value); - void setUniformValue(GLuint uniform, const QVector3D &value); - void setUniformValue(GLuint uniform, const QVector4D &value); - void setUniformValue(GLuint uniform, const QMatrix4x4 &value); - void setUniformValue(GLuint uniform, GLfloat value); - void setUniformValue(GLuint uniform, GLint value); - void setUniformValueArray(GLuint uniform, const QVector4D *values, int count); - - GLuint MVP(); - GLuint view(); - GLuint model(); - GLuint nModel(); - GLuint depth(); - GLuint lightP(); - GLuint lightS(); - GLuint ambientS(); - GLuint shadowQ(); - GLuint color(); - GLuint texture(); - GLuint shadow(); - GLuint gradientMin(); - GLuint gradientHeight(); - GLuint lightColor(); - GLuint volumeSliceIndices(); - GLuint colorIndex(); - GLuint cameraPositionRelativeToModel(); - GLuint color8Bit(); - GLuint textureDimensions(); - GLuint sampleCount(); - GLuint alphaMultiplier(); - GLuint preserveOpacity(); - GLuint maxBounds(); - GLuint minBounds(); - GLuint sliceFrameWidth(); - - GLuint posAtt(); - GLuint uvAtt(); - GLuint normalAtt(); + void setUniformValue(GLint uniform, const QVector2D &value); + void setUniformValue(GLint uniform, const QVector3D &value); + void setUniformValue(GLint uniform, const QVector4D &value); + void setUniformValue(GLint uniform, const QMatrix4x4 &value); + void setUniformValue(GLint uniform, GLfloat value); + void setUniformValue(GLint uniform, GLint value); + void setUniformValueArray(GLint uniform, const QVector4D *values, int count); + + GLint MVP(); + GLint view(); + GLint model(); + GLint nModel(); + GLint depth(); + GLint lightP(); + GLint lightS(); + GLint ambientS(); + GLint shadowQ(); + GLint color(); + GLint texture(); + GLint shadow(); + GLint gradientMin(); + GLint gradientHeight(); + GLint lightColor(); + GLint volumeSliceIndices(); + GLint colorIndex(); + GLint cameraPositionRelativeToModel(); + GLint color8Bit(); + GLint textureDimensions(); + GLint sampleCount(); + GLint alphaMultiplier(); + GLint preserveOpacity(); + GLint maxBounds(); + GLint minBounds(); + GLint sliceFrameWidth(); + + GLint posAtt(); + GLint uvAtt(); + GLint normalAtt(); private: QObject *m_caller; @@ -101,36 +101,36 @@ class ShaderHelper QString m_textureFile; QString m_depthTextureFile; - GLuint m_positionAttr; - GLuint m_uvAttr; - GLuint m_normalAttr; - - GLuint m_colorUniform; - GLuint m_viewMatrixUniform; - GLuint m_modelMatrixUniform; - GLuint m_invTransModelMatrixUniform; - GLuint m_depthMatrixUniform; - GLuint m_mvpMatrixUniform; - GLuint m_lightPositionUniform; - GLuint m_lightStrengthUniform; - GLuint m_ambientStrengthUniform; - GLuint m_shadowQualityUniform; - GLuint m_textureUniform; - GLuint m_shadowUniform; - GLuint m_gradientMinUniform; - GLuint m_gradientHeightUniform; - GLuint m_lightColorUniform; - GLuint m_volumeSliceIndicesUniform; - GLuint m_colorIndexUniform; - GLuint m_cameraPositionRelativeToModelUniform; - GLuint m_color8BitUniform; - GLuint m_textureDimensionsUniform; - GLuint m_sampleCountUniform; - GLuint m_alphaMultiplierUniform; - GLuint m_preserveOpacityUniform; - GLuint m_minBoundsUniform; - GLuint m_maxBoundsUniform; - GLuint m_sliceFrameWidthUniform; + GLint m_positionAttr; + GLint m_uvAttr; + GLint m_normalAttr; + + GLint m_colorUniform; + GLint m_viewMatrixUniform; + GLint m_modelMatrixUniform; + GLint m_invTransModelMatrixUniform; + GLint m_depthMatrixUniform; + GLint m_mvpMatrixUniform; + GLint m_lightPositionUniform; + GLint m_lightStrengthUniform; + GLint m_ambientStrengthUniform; + GLint m_shadowQualityUniform; + GLint m_textureUniform; + GLint m_shadowUniform; + GLint m_gradientMinUniform; + GLint m_gradientHeightUniform; + GLint m_lightColorUniform; + GLint m_volumeSliceIndicesUniform; + GLint m_colorIndexUniform; + GLint m_cameraPositionRelativeToModelUniform; + GLint m_color8BitUniform; + GLint m_textureDimensionsUniform; + GLint m_sampleCountUniform; + GLint m_alphaMultiplierUniform; + GLint m_preserveOpacityUniform; + GLint m_minBoundsUniform; + GLint m_maxBoundsUniform; + GLint m_sliceFrameWidthUniform; GLboolean m_initialized; }; -- cgit v1.2.3 From fc049fcbc210b1aa7a59cf8c02247adfa35e5355 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomi=20Korpip=C3=A4=C3=A4?= Date: Thu, 9 Oct 2014 12:28:57 +0300 Subject: Added doc about reflections and stencil buffer Change-Id: I939d02433291b00a529598718b6390d7246f89f9 Change-Id: I939d02433291b00a529598718b6390d7246f89f9 Reviewed-by: Miikka Heikkinen --- src/datavisualization/engine/qabstract3dgraph.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/datavisualization/engine/qabstract3dgraph.cpp b/src/datavisualization/engine/qabstract3dgraph.cpp index 9fb71d59..df6ed597 100644 --- a/src/datavisualization/engine/qabstract3dgraph.cpp +++ b/src/datavisualization/engine/qabstract3dgraph.cpp @@ -743,6 +743,9 @@ qreal QAbstract3DGraph::horizontalAspectRatio() const * for custom items that intersect the floor plane. In that case, reflections should be turned off * to avoid incorrect rendering. * + * \note If using custom surface format, stencil buffer needs to be defined + * (QSurfaceFormat::setStencilBufferSize()) for reflections to work. + * * \sa reflectivity */ void QAbstract3DGraph::setReflection(bool enable) -- cgit v1.2.3 From edbaf4a432bca084a01d739e16386a00919154b9 Mon Sep 17 00:00:00 2001 From: Mika Salmela Date: Thu, 9 Oct 2014 15:09:28 +0300 Subject: License info for texturedsurface example Change-Id: Iac6e8eac0da0264a586477038753c54edcd58e47 Reviewed-by: Mika Salmela --- .../datavisualization/texturesurface/license.txt | 77 ++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 examples/datavisualization/texturesurface/license.txt diff --git a/examples/datavisualization/texturesurface/license.txt b/examples/datavisualization/texturesurface/license.txt new file mode 100644 index 00000000..749daf31 --- /dev/null +++ b/examples/datavisualization/texturesurface/license.txt @@ -0,0 +1,77 @@ +License information regarding the data obtained from National Land Survey of +Finland http://www.maanmittauslaitos.fi/en +- topographic model from Elevation model 2 m (U4421B, U4421D, U4422A and + U4422C) 08/2014 +- map image extracted from Topographic map raster 1:50 000 (U442) 08/2014 + +National Land Survey open data licence - version 1.0 - 1 May 2012 + +1. General information + +The National Land Survey of Finland (hereinafter the Licensor), as the holder +of the immaterial rights to the data, has granted on the terms mentioned below +the right to use a copy (hereinafter data or dataset(s)) of the data (or a part +of it). + +The Licensee is a natural or legal person who makes use of the data covered by +this licence. The Licensee accepts the terms of this licence by receiving the +dataset(s) covered by the licence. + +This Licence agreement does not create a co-operation or business relationship +between the Licensee and the Licensor. + +2. Terms of the licence + +2.1. Right of use + +This licence grants a worldwide, free of charge and irrevocable parallel right +of use to open data. According to the terms of the licence, data received by +the Licensee can be freely: + - copied, distributed and published, + - modified and utilised commercially and non-commercially, + - inserted into other products and + - used as a part of a software application or service. + +2.2. Duties and responsibilities of the Licensee + +Through reasonable means suitable to the distribution medium or method which is +used in conjunction with a product containing data or a service utilising data +covered by this licence or while distributing data, the Licensee shall: + - mention the name of the Licensor, the name of the dataset(s) and the time + when the National Land Survey has delivered the dataset(s) (e.g.: contains + data from the National Land Survey of Finland Topographic Database 06/2012) + - provide a copy of this licence or a link to it, as well as + - require third parties to provide the same information when granting rights + to copies of dataset(s) or products and services containing such data and + - remove the name of the Licensor from the product or service, if required to + do so by the Licensor. + +The terms of this licence do not allow the Licensee to state in conjunction +with the use of dataset(s) that the Licensor supports or recommends such use. + +2.3. Duties and responsibilities of the Licensor + +The Licensor shall ensure that + - the Licensor has the right to grant rights to the dataset(s) in accordance + with this licence. + +The data has been licensed "as is" and the Licensor + - shall not be held responsible for any errors or omissions in the data, + disclaims any warranty for the validity or up to date status of the data and + shall be free from liability for direct or consequential damages arising + from the use of data provided by the Licensor, + - and is not obligated to ensure the continuous availability of the data, nor + to announce in advance the interruption or cessation of availability, and + the Licensor shall be free from liability for direct or consequential + damages arising from any such interruption or cessation. + +3. Jurisdiction + +Finnish law shall apply to this licence. + +4. Changes to this licence + +The Licensor may at any time change the terms of the licence or apply a +different licence to the data. The terms of this licence shall, however, still +apply to such data that has been received prior to the change of the terms of +the licence or the licence itself. -- cgit v1.2.3 From 93204b3bda941f1b36042fad46753c3f3219a082 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomi=20Korpip=C3=A4=C3=A4?= Date: Thu, 9 Oct 2014 13:20:14 +0300 Subject: Added initial QML autotests Just a skeleton for now. Change-Id: I27d577062e7e96880d843a87b71791f896bc2522 Change-Id: I27d577062e7e96880d843a87b71791f896bc2522 Reviewed-by: Miikka Heikkinen --- tests/auto/auto.pro | 4 +++ tests/auto/qmltest/bars3d/tst_basic.qml | 42 ++++++++++++++++++++++++++++++ tests/auto/qmltest/qmltest.pro | 8 ++++++ tests/auto/qmltest/scatter3d/tst_basic.qml | 42 ++++++++++++++++++++++++++++++ tests/auto/qmltest/surface3d/tst_basic.qml | 42 ++++++++++++++++++++++++++++++ tests/auto/qmltest/tst_qmltest.cpp | 38 +++++++++++++++++++++++++++ tests/tests.pro | 3 ++- 7 files changed, 178 insertions(+), 1 deletion(-) create mode 100644 tests/auto/auto.pro create mode 100644 tests/auto/qmltest/bars3d/tst_basic.qml create mode 100644 tests/auto/qmltest/qmltest.pro create mode 100644 tests/auto/qmltest/scatter3d/tst_basic.qml create mode 100644 tests/auto/qmltest/surface3d/tst_basic.qml create mode 100644 tests/auto/qmltest/tst_qmltest.cpp diff --git a/tests/auto/auto.pro b/tests/auto/auto.pro new file mode 100644 index 00000000..237e5116 --- /dev/null +++ b/tests/auto/auto.pro @@ -0,0 +1,4 @@ +TEMPLATE = subdirs +SUBDIRS = qmltest + +installed_cmake.depends = cmake diff --git a/tests/auto/qmltest/bars3d/tst_basic.qml b/tests/auto/qmltest/bars3d/tst_basic.qml new file mode 100644 index 00000000..bc4abd97 --- /dev/null +++ b/tests/auto/qmltest/bars3d/tst_basic.qml @@ -0,0 +1,42 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +import QtQuick 2.3 +import QtDataVisualization 1.2 +import QtTest 1.0 + +Item { + id: top + height: 150 + width: 150 + + Bars3D { + id: empty + } + + TestCase { + name: "Bars3D" + when: windowShown + + function test_empty() { + compare(empty.width, 0) + compare(empty.height, 0) + // TODO: Test default values of properties + } + } +} diff --git a/tests/auto/qmltest/qmltest.pro b/tests/auto/qmltest/qmltest.pro new file mode 100644 index 00000000..b2f977eb --- /dev/null +++ b/tests/auto/qmltest/qmltest.pro @@ -0,0 +1,8 @@ +TEMPLATE = app +TARGET = tst_qmltest +CONFIG += qmltestcase +CONFIG += console +SOURCES += tst_qmltest.cpp +OTHER_FILES += bars3d\tst_basic.qml \ + scatter3d\tst_basic.qml \ + surface3d\tst_basic.qml diff --git a/tests/auto/qmltest/scatter3d/tst_basic.qml b/tests/auto/qmltest/scatter3d/tst_basic.qml new file mode 100644 index 00000000..34a6bed1 --- /dev/null +++ b/tests/auto/qmltest/scatter3d/tst_basic.qml @@ -0,0 +1,42 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +import QtQuick 2.3 +import QtDataVisualization 1.2 +import QtTest 1.0 + +Item { + id: top + height: 150 + width: 150 + + Scatter3D { + id: empty + } + + TestCase { + name: "Scatter3D" + when: windowShown + + function test_empty() { + compare(empty.width, 0) + compare(empty.height, 0) + // TODO: Test default values of properties + } + } +} diff --git a/tests/auto/qmltest/surface3d/tst_basic.qml b/tests/auto/qmltest/surface3d/tst_basic.qml new file mode 100644 index 00000000..7d13e093 --- /dev/null +++ b/tests/auto/qmltest/surface3d/tst_basic.qml @@ -0,0 +1,42 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +import QtQuick 2.3 +import QtDataVisualization 1.2 +import QtTest 1.0 + +Item { + id: top + height: 150 + width: 150 + + Surface3D { + id: empty + } + + TestCase { + name: "Surface3D" + when: windowShown + + function test_empty() { + compare(empty.width, 0) + compare(empty.height, 0) + // TODO: Test default values of properties + } + } +} diff --git a/tests/auto/qmltest/tst_qmltest.cpp b/tests/auto/qmltest/tst_qmltest.cpp new file mode 100644 index 00000000..44c1ddea --- /dev/null +++ b/tests/auto/qmltest/tst_qmltest.cpp @@ -0,0 +1,38 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +QUICK_TEST_MAIN(qmltest) diff --git a/tests/tests.pro b/tests/tests.pro index 9073ca9f..e5364a0e 100644 --- a/tests/tests.pro +++ b/tests/tests.pro @@ -18,7 +18,8 @@ SUBDIRS += barstest \ itemmodeltest \ qmlmultitest \ volumetrictest \ - qmlvolume + qmlvolume \ + auto #SUBDIRS += kinectsurface -- cgit v1.2.3 From 73b0741d2aad38a5e7e880a3731f68a320aab280 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Thu, 9 Oct 2014 14:48:19 +0300 Subject: If GL_MAX_TEXTURE_SIZE cannot be read, don't check for it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In QML apps, the texture for custom labels can get created before glGenIntegerv returns a valid value for GL_MAX_TEXTURE_SIZE. Do not check for max size when it is zero. Change-Id: I114ac8494bd5aa509490bd38ae58827fd21e2729 Reviewed-by: Mika Salmela Reviewed-by: Tomi Korpipää --- src/datavisualization/utils/utils.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/datavisualization/utils/utils.cpp b/src/datavisualization/utils/utils.cpp index d588ee16..2767451e 100644 --- a/src/datavisualization/utils/utils.cpp +++ b/src/datavisualization/utils/utils.cpp @@ -94,7 +94,7 @@ QImage Utils::printTextToImage(const QFont &font, const QString &text, const QCo else labelSize = QSize(valueStrWidth + paddingWidth * 2, valueStrHeight + paddingHeight * 2); #endif - if (labelSize.width() <= maxTextureSize) { + if (!maxTextureSize || labelSize.width() <= maxTextureSize) { // Make sure the label is not too wide sizeOk = true; } else if (--currentFontSize == 4) { -- cgit v1.2.3 From cb5ab9bc21cd123efc9f78512f465794bcb4a964 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomi=20Korpip=C3=A4=C3=A4?= Date: Fri, 10 Oct 2014 11:38:49 +0300 Subject: Basic autotests for Bars3D, Scatter3D and Surface3D Change-Id: I36b384d2bcb9d2db2464b3eaaaae2c76c3bb740c Reviewed-by: Miikka Heikkinen --- tests/auto/qmltest/bars3d/tst_basic.qml | 195 ++++++++++++++++++++++++++++- tests/auto/qmltest/scatter3d/tst_basic.qml | 158 ++++++++++++++++++++++- tests/auto/qmltest/surface3d/tst_basic.qml | 166 +++++++++++++++++++++++- 3 files changed, 501 insertions(+), 18 deletions(-) diff --git a/tests/auto/qmltest/bars3d/tst_basic.qml b/tests/auto/qmltest/bars3d/tst_basic.qml index bc4abd97..6c908623 100644 --- a/tests/auto/qmltest/bars3d/tst_basic.qml +++ b/tests/auto/qmltest/bars3d/tst_basic.qml @@ -16,7 +16,7 @@ ** ****************************************************************************/ -import QtQuick 2.3 +import QtQuick 2.0 import QtDataVisualization 1.2 import QtTest 1.0 @@ -29,14 +29,197 @@ Item { id: empty } + Bars3D { + id: basic + anchors.fill: parent + multiSeriesUniform: true + barThickness: 0.1 + barSpacing.width: 0.1 + barSpacing.height: 0.1 + barSpacingRelative: false + floorLevel: 1.0 + } + + Bars3D { + id: common + anchors.fill: parent + } + + Bars3D { + id: common_init + anchors.fill: parent + selectionMode: AbstractGraph3D.SelectionNone + shadowQuality: AbstractGraph3D.ShadowQualityLow + msaaSamples: 2 + theme: Theme3D { } + renderingMode: AbstractGraph3D.RenderIndirect + measureFps: true + orthoProjection: false + aspectRatio: 3.0 + optimizationHints: AbstractGraph3D.OptimizationStatic + polar: false + radialLabelOffset: 2 + horizontalAspectRatio: 0.2 + reflection: true + reflectivity: 0.1 + locale: Qt.locale("UK") + margin: 0.2 + } + TestCase { - name: "Bars3D" - when: windowShown + name: "Bars3D Empty" function test_empty() { - compare(empty.width, 0) - compare(empty.height, 0) - // TODO: Test default values of properties + compare(empty.width, 0, "width") + compare(empty.height, 0, "height") + compare(empty.multiSeriesUniform, false, "multiSeriesUniform") + compare(empty.barThickness, 1.0, "barThickness") + compare(empty.barSpacing, Qt.size(0.2, 0.2), "barSpacing") + compare(empty.barSpacingRelative, true, "barSpacingRelative") + compare(empty.seriesList.length, 0, "seriesList") + compare(empty.selectedSeries, null, "selectedSeries") + compare(empty.primarySeries, null, "primarySeries") + compare(empty.floorLevel, 0.0, "floorLevel") + } + } + + TestCase { + name: "Bars3D Basic" + when: windowShown + + function test_basic() { + compare(basic.width, 150, "width") + compare(basic.height, 150, "height") + compare(basic.multiSeriesUniform, true, "multiSeriesUniform") + compare(basic.barThickness, 0.1, "barThickness") + compare(basic.barSpacing, Qt.size(0.1, 0.1), "barSpacing") + compare(basic.barSpacingRelative, false, "barSpacingRelative") + compare(basic.floorLevel, 1.0, "floorLevel") + } + + function test_change_basic() { + basic.multiSeriesUniform = false + basic.barThickness = 0.5 + basic.barSpacing = Qt.size(1.0, 0.0) + basic.barSpacingRelative = true + basic.floorLevel = 0.2 + compare(basic.multiSeriesUniform, false, "multiSeriesUniform") + compare(basic.barThickness, 0.5, "barThickness") + compare(basic.barSpacing, Qt.size(1.0, 0.0), "barSpacing") + compare(basic.barSpacingRelative, true, "barSpacingRelative") + compare(basic.floorLevel, 0.2, "floorLevel") + } + + function test_change_invalid_basic() { + basic.barThickness = -1 + basic.barSpacing = Qt.size(-1.0, -1.0) + compare(basic.barThickness, -1/*0.5*/, "barThickness") // TODO: Fix once QTRD-3367 is done + compare(basic.barSpacing, Qt.size(1.0, 0.0), "barSpacing") + } + } + + TestCase { + name: "Bars3D Common" + when: windowShown + + function test_1_common() { + compare(common.selectionMode, AbstractGraph3D.SelectionItem, "selectionMode") + compare(common.shadowQuality, AbstractGraph3D.ShadowQualityMedium, "shadowQuality") + compare(common.shadowsSupported, true, "shadowsSupported") + compare(common.msaaSamples, 4, "msaaSamples") + compare(common.theme.type, Theme3D.ThemeQt, "theme") + compare(common.renderingMode, AbstractGraph3D.RenderIndirect, "renderingMode") + compare(common.measureFps, false, "measureFps") + compare(common.customItemList.length, 0, "customItemList") + compare(common.orthoProjection, false, "orthoProjection") + compare(common.selectedElement, AbstractGraph3D.ElementNone, "selectedElement") + compare(common.aspectRatio, 2.0, "aspectRatio") + compare(common.optimizationHints, AbstractGraph3D.OptimizationDefault, "optimizationHints") + compare(common.polar, false, "polar") + compare(common.radialLabelOffset, 1, "radialLabelOffset") + compare(common.horizontalAspectRatio, 0, "horizontalAspectRatio") + compare(common.reflection, false, "reflection") + compare(common.reflectivity, 0.5, "reflectivity") + compare(common.locale, Qt.locale("C"), "locale") + compare(common.queriedGraphPosition, Qt.vector3d(0, 0, 0), "queriedGraphPosition") + compare(common.margin, -1, "margin") + } + + function test_2_change_common() { + common.selectionMode = AbstractGraph3D.SelectionItem | AbstractGraph3D.SelectionRow | AbstractGraph3D.SelectionSlice + common.shadowQuality = AbstractGraph3D.ShadowQualitySoftHigh + compare(common.shadowQuality, AbstractGraph3D.ShadowQualitySoftHigh, "shadowQuality") + common.msaaSamples = 8 + compare(common.msaaSamples, 8, "msaaSamples") + common.theme.type = Theme3D.ThemeRetro + common.renderingMode = AbstractGraph3D.RenderDirectToBackground_NoClear + common.measureFps = true + common.orthoProjection = true + common.aspectRatio = 1.0 + common.optimizationHints = AbstractGraph3D.OptimizationStatic + common.polar = true + common.radialLabelOffset = 2 + common.horizontalAspectRatio = 1 + common.reflection = true + common.reflectivity = 1.0 + common.locale = Qt.locale("FI") + common.margin = 1.0 + compare(common.selectionMode, AbstractGraph3D.SelectionItem | AbstractGraph3D.SelectionRow | AbstractGraph3D.SelectionSlice, "selectionMode") + compare(common.shadowQuality, AbstractGraph3D.ShadowQualityNone, "shadowQuality") // Ortho disables shadows + compare(common.msaaSamples, 0, "msaaSamples") // Rendering mode changes this to zero + compare(common.theme.type, Theme3D.ThemeRetro, "theme") + compare(common.renderingMode, AbstractGraph3D.RenderDirectToBackground_NoClear, "renderingMode") + compare(common.measureFps, true, "measureFps") + compare(common.orthoProjection, true, "orthoProjection") + compare(common.aspectRatio, 1.0, "aspectRatio") + compare(common.optimizationHints, AbstractGraph3D.OptimizationStatic, "optimizationHints") + compare(common.polar, true, "polar") + compare(common.radialLabelOffset, 2, "radialLabelOffset") + compare(common.horizontalAspectRatio, 1, "horizontalAspectRatio") + compare(common.reflection, true, "reflection") + compare(common.reflectivity, 1.0, "reflectivity") + compare(common.locale, Qt.locale("FI"), "locale") + compare(common.margin, 1.0, "margin") + } + + function test_3_change_invalid_common() { + common.selectionMode = AbstractGraph3D.SelectionRow | AbstractGraph3D.SelectionColumn | AbstractGraph3D.SelectionSlice + common.theme.type = -2 + common.renderingMode = -1 + common.measureFps = false + common.orthoProjection = false + common.aspectRatio = -1.0 + common.polar = false + common.horizontalAspectRatio = -2 + common.reflection = false + common.reflectivity = -1.0 + compare(common.selectionMode, AbstractGraph3D.SelectionItem | AbstractGraph3D.SelectionRow | AbstractGraph3D.SelectionSlice, "selectionMode") + compare(common.theme.type, -2/*Theme3D.ThemeRetro*/, "theme") // TODO: Fix once QTRD-3367 is done + compare(common.renderingMode, -1/*AbstractGraph3D.RenderDirectToBackground_NoClear*/, "renderingMode") // TODO: Fix once QTRD-3367 is done + compare(common.aspectRatio, -1.0/*1.0*/, "aspectRatio") // TODO: Fix once QTRD-3367 is done + compare(common.horizontalAspectRatio, -2/*1*/, "horizontalAspectRatio") // TODO: Fix once QTRD-3367 is done + compare(common.reflectivity, -1.0/*1.0*/, "reflectivity") // TODO: Fix once QTRD-3367 is done + } + + function test_4_common_initialized() { + compare(common_init.selectionMode, AbstractGraph3D.SelectionNone, "selectionMode") + compare(common_init.shadowQuality, AbstractGraph3D.ShadowQualityLow, "shadowQuality") + compare(common_init.shadowsSupported, true, "shadowsSupported") + compare(common_init.msaaSamples, 2, "msaaSamples") + compare(common_init.theme.type, Theme3D.ThemeUserDefined, "theme") + compare(common_init.renderingMode, AbstractGraph3D.RenderIndirect, "renderingMode") + compare(common_init.measureFps, true, "measureFps") + compare(common_init.customItemList.length, 0, "customItemList") + compare(common_init.orthoProjection, false, "orthoProjection") + compare(common_init.aspectRatio, 3.0, "aspectRatio") + compare(common_init.optimizationHints, AbstractGraph3D.OptimizationStatic, "optimizationHints") + compare(common_init.polar, false, "polar") + compare(common_init.radialLabelOffset, 2, "radialLabelOffset") + compare(common_init.horizontalAspectRatio, 0.2, "horizontalAspectRatio") + compare(common_init.reflection, true, "reflection") + compare(common_init.reflectivity, 0.1, "reflectivity") + compare(common_init.locale, Qt.locale("UK"), "locale") + compare(common_init.margin, 0.2, "margin") } } } diff --git a/tests/auto/qmltest/scatter3d/tst_basic.qml b/tests/auto/qmltest/scatter3d/tst_basic.qml index 34a6bed1..b9d9c93c 100644 --- a/tests/auto/qmltest/scatter3d/tst_basic.qml +++ b/tests/auto/qmltest/scatter3d/tst_basic.qml @@ -16,7 +16,7 @@ ** ****************************************************************************/ -import QtQuick 2.3 +import QtQuick 2.0 import QtDataVisualization 1.2 import QtTest 1.0 @@ -29,14 +29,160 @@ Item { id: empty } + Scatter3D { + id: basic + anchors.fill: parent + } + + Scatter3D { + id: common + anchors.fill: parent + } + + Scatter3D { + id: common_init + anchors.fill: parent + selectionMode: AbstractGraph3D.SelectionNone + shadowQuality: AbstractGraph3D.ShadowQualityLow + msaaSamples: 2 + theme: Theme3D { } + renderingMode: AbstractGraph3D.RenderIndirect + measureFps: true + orthoProjection: false + aspectRatio: 3.0 + optimizationHints: AbstractGraph3D.OptimizationStatic + polar: false + radialLabelOffset: 2 + horizontalAspectRatio: 0.2 + reflection: true + reflectivity: 0.1 + locale: Qt.locale("UK") + margin: 0.2 + } + TestCase { - name: "Scatter3D" - when: windowShown + name: "Scatter3D Empty" function test_empty() { - compare(empty.width, 0) - compare(empty.height, 0) - // TODO: Test default values of properties + compare(empty.width, 0, "width") + compare(empty.height, 0, "height") + compare(empty.seriesList.length, 0, "seriesList") + compare(empty.selectedSeries, null, "selectedSeries") + } + } + + TestCase { + name: "Scatter3D Basic" + when: windowShown + + function test_basic() { + compare(basic.width, 150, "width") + compare(basic.height, 150, "height") + } + } + + TestCase { + name: "Scatter3D Common" + when: windowShown + + function test_1_common() { + compare(common.selectionMode, AbstractGraph3D.SelectionItem, "selectionMode") + compare(common.shadowQuality, AbstractGraph3D.ShadowQualityMedium, "shadowQuality") + compare(common.shadowsSupported, true, "shadowsSupported") + compare(common.msaaSamples, 4, "msaaSamples") + compare(common.theme.type, Theme3D.ThemeQt, "theme") + compare(common.renderingMode, AbstractGraph3D.RenderIndirect, "renderingMode") + compare(common.measureFps, false, "measureFps") + compare(common.customItemList.length, 0, "customItemList") + compare(common.orthoProjection, false, "orthoProjection") + compare(common.selectedElement, AbstractGraph3D.ElementNone, "selectedElement") + compare(common.aspectRatio, 2.0, "aspectRatio") + compare(common.optimizationHints, AbstractGraph3D.OptimizationDefault, "optimizationHints") + compare(common.polar, false, "polar") + compare(common.radialLabelOffset, 1, "radialLabelOffset") + compare(common.horizontalAspectRatio, 0, "horizontalAspectRatio") + compare(common.reflection, false, "reflection") + compare(common.reflectivity, 0.5, "reflectivity") + compare(common.locale, Qt.locale("C"), "locale") + compare(common.queriedGraphPosition, Qt.vector3d(0, 0, 0), "queriedGraphPosition") + compare(common.margin, -1, "margin") + } + + function test_2_change_common() { + common.selectionMode = AbstractGraph3D.SelectionNone + common.shadowQuality = AbstractGraph3D.ShadowQualitySoftHigh + compare(common.shadowQuality, AbstractGraph3D.ShadowQualitySoftHigh, "shadowQuality") + common.msaaSamples = 8 + compare(common.msaaSamples, 8, "msaaSamples") + common.theme.type = Theme3D.ThemeRetro + common.renderingMode = AbstractGraph3D.RenderDirectToBackground_NoClear + common.measureFps = true + common.orthoProjection = true + common.aspectRatio = 1.0 + common.optimizationHints = AbstractGraph3D.OptimizationStatic + common.polar = true + common.radialLabelOffset = 2 + common.horizontalAspectRatio = 1 + common.reflection = true + common.reflectivity = 1.0 + common.locale = Qt.locale("FI") + common.margin = 1.0 + compare(common.selectionMode, AbstractGraph3D.SelectionNone, "selectionMode") + compare(common.shadowQuality, AbstractGraph3D.ShadowQualityNone, "shadowQuality") // Ortho disables shadows + compare(common.msaaSamples, 0, "msaaSamples") // Rendering mode changes this to zero + compare(common.theme.type, Theme3D.ThemeRetro, "theme") + compare(common.renderingMode, AbstractGraph3D.RenderDirectToBackground_NoClear, "renderingMode") + compare(common.measureFps, true, "measureFps") + compare(common.orthoProjection, true, "orthoProjection") + compare(common.aspectRatio, 1.0, "aspectRatio") + compare(common.optimizationHints, AbstractGraph3D.OptimizationStatic, "optimizationHints") + compare(common.polar, true, "polar") + compare(common.radialLabelOffset, 2, "radialLabelOffset") + compare(common.horizontalAspectRatio, 1, "horizontalAspectRatio") + compare(common.reflection, true, "reflection") + compare(common.reflectivity, 1.0, "reflectivity") + compare(common.locale, Qt.locale("FI"), "locale") + compare(common.margin, 1.0, "margin") + } + + function test_3_change_invalid_common() { + common.selectionMode = AbstractGraph3D.SelectionRow | AbstractGraph3D.SelectionColumn | AbstractGraph3D.SelectionSlice + common.theme.type = -2 + common.renderingMode = -1 + common.measureFps = false + common.orthoProjection = false + common.aspectRatio = -1.0 + common.polar = false + common.horizontalAspectRatio = -2 + common.reflection = false + common.reflectivity = -1.0 + compare(common.selectionMode, AbstractGraph3D.SelectionNone, "selectionMode") + compare(common.theme.type, -2/*Theme3D.ThemeRetro*/, "theme") // TODO: Fix once QTRD-3367 is done + compare(common.renderingMode, -1/*AbstractGraph3D.RenderDirectToBackground_NoClear*/, "renderingMode") // TODO: Fix once QTRD-3367 is done + compare(common.aspectRatio, -1.0/*1.0*/, "aspectRatio") // TODO: Fix once QTRD-3367 is done + compare(common.horizontalAspectRatio, -2/*1*/, "horizontalAspectRatio") // TODO: Fix once QTRD-3367 is done + compare(common.reflectivity, -1.0/*1.0*/, "reflectivity") // TODO: Fix once QTRD-3367 is done + } + + function test_4_common_initialized() { + compare(common_init.selectionMode, AbstractGraph3D.SelectionNone, "selectionMode") + compare(common_init.shadowQuality, AbstractGraph3D.ShadowQualityLow, "shadowQuality") + compare(common_init.shadowsSupported, true, "shadowsSupported") + compare(common_init.msaaSamples, 2, "msaaSamples") + compare(common_init.theme.type, Theme3D.ThemeUserDefined, "theme") + compare(common_init.renderingMode, AbstractGraph3D.RenderIndirect, "renderingMode") + compare(common_init.measureFps, true, "measureFps") + compare(common_init.customItemList.length, 0, "customItemList") + compare(common_init.orthoProjection, false, "orthoProjection") + compare(common_init.aspectRatio, 3.0, "aspectRatio") + compare(common_init.optimizationHints, AbstractGraph3D.OptimizationStatic, "optimizationHints") + compare(common_init.polar, false, "polar") + compare(common_init.radialLabelOffset, 2, "radialLabelOffset") + compare(common_init.horizontalAspectRatio, 0.2, "horizontalAspectRatio") + compare(common_init.reflection, true, "reflection") + compare(common_init.reflectivity, 0.1, "reflectivity") + compare(common_init.locale, Qt.locale("UK"), "locale") + compare(common_init.margin, 0.2, "margin") } } } diff --git a/tests/auto/qmltest/surface3d/tst_basic.qml b/tests/auto/qmltest/surface3d/tst_basic.qml index 7d13e093..17d3d614 100644 --- a/tests/auto/qmltest/surface3d/tst_basic.qml +++ b/tests/auto/qmltest/surface3d/tst_basic.qml @@ -16,7 +16,7 @@ ** ****************************************************************************/ -import QtQuick 2.3 +import QtQuick 2.0 import QtDataVisualization 1.2 import QtTest 1.0 @@ -29,14 +29,168 @@ Item { id: empty } + Surface3D { + id: basic + anchors.fill: parent + flipHorizontalGrid: true + } + + Surface3D { + id: common + anchors.fill: parent + } + + Surface3D { + id: common_init + anchors.fill: parent + selectionMode: AbstractGraph3D.SelectionNone + shadowQuality: AbstractGraph3D.ShadowQualityLow + msaaSamples: 2 + theme: Theme3D { } + renderingMode: AbstractGraph3D.RenderIndirect + measureFps: true + orthoProjection: false + aspectRatio: 3.0 + optimizationHints: AbstractGraph3D.OptimizationStatic + polar: false + radialLabelOffset: 2 + horizontalAspectRatio: 0.2 + reflection: true + reflectivity: 0.1 + locale: Qt.locale("UK") + margin: 0.2 + } + TestCase { - name: "Surface3D" - when: windowShown + name: "Surface3D Empty" function test_empty() { - compare(empty.width, 0) - compare(empty.height, 0) - // TODO: Test default values of properties + compare(empty.width, 0, "width") + compare(empty.height, 0, "height") + compare(empty.seriesList.length, 0, "seriesList") + compare(empty.selectedSeries, null, "selectedSeries") + compare(empty.flipHorizontalGrid, false, "flipHorizontalGrid") + } + } + + TestCase { + name: "Surface3D Basic" + when: windowShown + + function test_basic() { + compare(basic.width, 150, "width") + compare(basic.height, 150, "height") + compare(basic.flipHorizontalGrid, true, "flipHorizontalGrid") + } + + function test_change_basic() { + basic.flipHorizontalGrid = false + compare(basic.flipHorizontalGrid, false, "flipHorizontalGrid") + } + } + + TestCase { + name: "Surface3D Common" + when: windowShown + + function test_1_common() { + compare(common.selectionMode, AbstractGraph3D.SelectionItem, "selectionMode") + compare(common.shadowQuality, AbstractGraph3D.ShadowQualityMedium, "shadowQuality") + compare(common.shadowsSupported, true, "shadowsSupported") + compare(common.msaaSamples, 4, "msaaSamples") + compare(common.theme.type, Theme3D.ThemeQt, "theme") + compare(common.renderingMode, AbstractGraph3D.RenderIndirect, "renderingMode") + compare(common.measureFps, false, "measureFps") + compare(common.customItemList.length, 0, "customItemList") + compare(common.orthoProjection, false, "orthoProjection") + compare(common.selectedElement, AbstractGraph3D.ElementNone, "selectedElement") + compare(common.aspectRatio, 2.0, "aspectRatio") + compare(common.optimizationHints, AbstractGraph3D.OptimizationDefault, "optimizationHints") + compare(common.polar, false, "polar") + compare(common.radialLabelOffset, 1, "radialLabelOffset") + compare(common.horizontalAspectRatio, 0, "horizontalAspectRatio") + compare(common.reflection, false, "reflection") + compare(common.reflectivity, 0.5, "reflectivity") + compare(common.locale, Qt.locale("C"), "locale") + compare(common.queriedGraphPosition, Qt.vector3d(0, 0, 0), "queriedGraphPosition") + compare(common.margin, -1, "margin") + } + + function test_2_change_common() { + common.selectionMode = AbstractGraph3D.SelectionItem | AbstractGraph3D.SelectionRow | AbstractGraph3D.SelectionSlice + common.shadowQuality = AbstractGraph3D.ShadowQualitySoftHigh + compare(common.shadowQuality, AbstractGraph3D.ShadowQualitySoftHigh, "shadowQuality") + common.msaaSamples = 8 + compare(common.msaaSamples, 8, "msaaSamples") + common.theme.type = Theme3D.ThemeRetro + common.renderingMode = AbstractGraph3D.RenderDirectToBackground_NoClear + common.measureFps = true + common.orthoProjection = true + common.aspectRatio = 1.0 + common.optimizationHints = AbstractGraph3D.OptimizationStatic + common.polar = true + common.radialLabelOffset = 2 + common.horizontalAspectRatio = 1 + common.reflection = true + common.reflectivity = 1.0 + common.locale = Qt.locale("FI") + common.margin = 1.0 + compare(common.selectionMode, AbstractGraph3D.SelectionItem | AbstractGraph3D.SelectionRow | AbstractGraph3D.SelectionSlice, "selectionMode") + compare(common.shadowQuality, AbstractGraph3D.ShadowQualityNone, "shadowQuality") // Ortho disables shadows + compare(common.msaaSamples, 0, "msaaSamples") // Rendering mode changes this to zero + compare(common.theme.type, Theme3D.ThemeRetro, "theme") + compare(common.renderingMode, AbstractGraph3D.RenderDirectToBackground_NoClear, "renderingMode") + compare(common.measureFps, true, "measureFps") + compare(common.orthoProjection, true, "orthoProjection") + compare(common.aspectRatio, 1.0, "aspectRatio") + compare(common.optimizationHints, AbstractGraph3D.OptimizationStatic, "optimizationHints") + compare(common.polar, true, "polar") + compare(common.radialLabelOffset, 2, "radialLabelOffset") + compare(common.horizontalAspectRatio, 1, "horizontalAspectRatio") + compare(common.reflection, true, "reflection") + compare(common.reflectivity, 1.0, "reflectivity") + compare(common.locale, Qt.locale("FI"), "locale") + compare(common.margin, 1.0, "margin") + } + + function test_3_change_invalid_common() { + common.selectionMode = AbstractGraph3D.SelectionRow | AbstractGraph3D.SelectionColumn | AbstractGraph3D.SelectionSlice + common.theme.type = -2 + common.renderingMode = -1 + common.measureFps = false + common.orthoProjection = false + common.aspectRatio = -1.0 + common.polar = false + common.horizontalAspectRatio = -2 + common.reflection = false + common.reflectivity = -1.0 + compare(common.selectionMode, AbstractGraph3D.SelectionItem | AbstractGraph3D.SelectionRow | AbstractGraph3D.SelectionSlice, "selectionMode") + compare(common.theme.type, -2/*Theme3D.ThemeRetro*/, "theme") // TODO: Fix once QTRD-3367 is done + compare(common.renderingMode, -1/*AbstractGraph3D.RenderDirectToBackground_NoClear*/, "renderingMode") // TODO: Fix once QTRD-3367 is done + compare(common.aspectRatio, -1.0/*1.0*/, "aspectRatio") // TODO: Fix once QTRD-3367 is done + compare(common.horizontalAspectRatio, -2/*1*/, "horizontalAspectRatio") // TODO: Fix once QTRD-3367 is done + compare(common.reflectivity, -1.0/*1.0*/, "reflectivity") // TODO: Fix once QTRD-3367 is done + } + + function test_4_common_initialized() { + compare(common_init.selectionMode, AbstractGraph3D.SelectionNone, "selectionMode") + compare(common_init.shadowQuality, AbstractGraph3D.ShadowQualityLow, "shadowQuality") + compare(common_init.shadowsSupported, true, "shadowsSupported") + compare(common_init.msaaSamples, 2, "msaaSamples") + compare(common_init.theme.type, Theme3D.ThemeUserDefined, "theme") + compare(common_init.renderingMode, AbstractGraph3D.RenderIndirect, "renderingMode") + compare(common_init.measureFps, true, "measureFps") + compare(common_init.customItemList.length, 0, "customItemList") + compare(common_init.orthoProjection, false, "orthoProjection") + compare(common_init.aspectRatio, 3.0, "aspectRatio") + compare(common_init.optimizationHints, AbstractGraph3D.OptimizationStatic, "optimizationHints") + compare(common_init.polar, false, "polar") + compare(common_init.radialLabelOffset, 2, "radialLabelOffset") + compare(common_init.horizontalAspectRatio, 0.2, "horizontalAspectRatio") + compare(common_init.reflection, true, "reflection") + compare(common_init.reflectivity, 0.1, "reflectivity") + compare(common_init.locale, Qt.locale("UK"), "locale") + compare(common_init.margin, 0.2, "margin") } } } -- cgit v1.2.3 From 220f236a18967f7dbc1add6425483ad885b1b22e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomi=20Korpip=C3=A4=C3=A4?= Date: Fri, 10 Oct 2014 13:21:39 +0300 Subject: Added initial CPP autotests Task-number: QTRD-3368 Just a skeleton with a very basic test set for now. Change-Id: I53309374a7d9cbfd06a4cdfbeb302f6649db9c4a Reviewed-by: Miikka Heikkinen --- tests/auto/auto.pro | 3 +- tests/auto/cpptest/cpptest.pro | 4 + tests/auto/cpptest/q3dbars/q3dbars.pro | 8 ++ tests/auto/cpptest/q3dbars/tst_bars.cpp | 108 ++++++++++++++++++++++++++ tests/auto/cpptest/q3dscatter/q3dscatter.pro | 8 ++ tests/auto/cpptest/q3dscatter/tst_scatter.cpp | 102 ++++++++++++++++++++++++ tests/auto/cpptest/q3dsurface/q3dsurface.pro | 8 ++ tests/auto/cpptest/q3dsurface/tst_surface.cpp | 103 ++++++++++++++++++++++++ tests/auto/qmltest/tst_qmltest.cpp | 36 +++------ 9 files changed, 352 insertions(+), 28 deletions(-) create mode 100644 tests/auto/cpptest/cpptest.pro create mode 100644 tests/auto/cpptest/q3dbars/q3dbars.pro create mode 100644 tests/auto/cpptest/q3dbars/tst_bars.cpp create mode 100644 tests/auto/cpptest/q3dscatter/q3dscatter.pro create mode 100644 tests/auto/cpptest/q3dscatter/tst_scatter.cpp create mode 100644 tests/auto/cpptest/q3dsurface/q3dsurface.pro create mode 100644 tests/auto/cpptest/q3dsurface/tst_surface.cpp diff --git a/tests/auto/auto.pro b/tests/auto/auto.pro index 237e5116..6f70bd1b 100644 --- a/tests/auto/auto.pro +++ b/tests/auto/auto.pro @@ -1,4 +1,5 @@ TEMPLATE = subdirs -SUBDIRS = qmltest +SUBDIRS = qmltest \ + cpptest installed_cmake.depends = cmake diff --git a/tests/auto/cpptest/cpptest.pro b/tests/auto/cpptest/cpptest.pro new file mode 100644 index 00000000..6d52a766 --- /dev/null +++ b/tests/auto/cpptest/cpptest.pro @@ -0,0 +1,4 @@ +TEMPLATE = subdirs +SUBDIRS = q3dbars \ + q3dscatter \ + q3dsurface diff --git a/tests/auto/cpptest/q3dbars/q3dbars.pro b/tests/auto/cpptest/q3dbars/q3dbars.pro new file mode 100644 index 00000000..a7f7c809 --- /dev/null +++ b/tests/auto/cpptest/q3dbars/q3dbars.pro @@ -0,0 +1,8 @@ +QT += testlib datavisualization + +TARGET = tst_cpptest +CONFIG += console testcase + +TEMPLATE = app + +SOURCES += tst_bars.cpp diff --git a/tests/auto/cpptest/q3dbars/tst_bars.cpp b/tests/auto/cpptest/q3dbars/tst_bars.cpp new file mode 100644 index 00000000..5422e7fc --- /dev/null +++ b/tests/auto/cpptest/q3dbars/tst_bars.cpp @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +#include + +#include + +using namespace QtDataVisualization; + +class tst_bars: public QObject +{ + Q_OBJECT + +private slots: + void initTestCase(); + void cleanupTestCase(); + void init(); + void cleanup(); + void construct(); + void initialProperties(); + void initializeProperties(); + void updateProperties(); + +private: + Q3DBars *m_graph; +}; + +void tst_bars::initTestCase() +{ +} + +void tst_bars::cleanupTestCase() +{ +} + +void tst_bars::init() +{ + m_graph = new Q3DBars(); +} + +void tst_bars::cleanup() +{ + delete m_graph; +} + +void tst_bars::construct() +{ + Q3DBars *graph = new Q3DBars(); + QVERIFY(graph); + delete graph; +} + +void tst_bars::initialProperties() +{ + QVERIFY(m_graph); + QCOMPARE(m_graph->isMultiSeriesUniform(), false); + QCOMPARE(m_graph->barThickness(), 1.0); + QCOMPARE(m_graph->barSpacing(), QSizeF(1.0f, 1.0f)); + QCOMPARE(m_graph->isBarSpacingRelative(), true); + QCOMPARE(m_graph->seriesList().length(), 0); + QVERIFY(!m_graph->selectedSeries()); + QVERIFY(!m_graph->primarySeries()); + QCOMPARE(m_graph->floorLevel(), 0.0); + + // Common properties + QCOMPARE(m_graph->activeTheme()->type(), Q3DTheme::ThemeQt); + QCOMPARE(m_graph->selectionMode(), QAbstract3DGraph::SelectionItem); + QCOMPARE(m_graph->shadowQuality(), QAbstract3DGraph::ShadowQualityMedium); + QCOMPARE(m_graph->measureFps(), false); + QCOMPARE(m_graph->isOrthoProjection(), false); + QCOMPARE(m_graph->selectedElement(), QAbstract3DGraph::ElementNone); + QCOMPARE(m_graph->aspectRatio(), 2.0); + QCOMPARE(m_graph->optimizationHints(), QAbstract3DGraph::OptimizationDefault); + QCOMPARE(m_graph->isPolar(), false); + QCOMPARE(m_graph->radialLabelOffset(), 1.0); + QCOMPARE(m_graph->horizontalAspectRatio(), 0.0); + QCOMPARE(m_graph->isReflection(), false); + QCOMPARE(m_graph->reflectivity(), 0.5); + QCOMPARE(m_graph->locale(), QLocale("C")); + QCOMPARE(m_graph->queriedGraphPosition(), QVector3D(0, 0, 0)); + QCOMPARE(m_graph->margin(), -1.0); +} + +void tst_bars::initializeProperties() +{ +} + +void tst_bars::updateProperties() +{ +} + +QTEST_MAIN(tst_bars) +#include "tst_bars.moc" diff --git a/tests/auto/cpptest/q3dscatter/q3dscatter.pro b/tests/auto/cpptest/q3dscatter/q3dscatter.pro new file mode 100644 index 00000000..9f356ebc --- /dev/null +++ b/tests/auto/cpptest/q3dscatter/q3dscatter.pro @@ -0,0 +1,8 @@ +QT += testlib datavisualization + +TARGET = tst_cpptest +CONFIG += console testcase + +TEMPLATE = app + +SOURCES += tst_scatter.cpp diff --git a/tests/auto/cpptest/q3dscatter/tst_scatter.cpp b/tests/auto/cpptest/q3dscatter/tst_scatter.cpp new file mode 100644 index 00000000..2ec0952e --- /dev/null +++ b/tests/auto/cpptest/q3dscatter/tst_scatter.cpp @@ -0,0 +1,102 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +#include + +#include + +using namespace QtDataVisualization; + +class tst_scatter: public QObject +{ + Q_OBJECT + +private slots: + void initTestCase(); + void cleanupTestCase(); + void init(); + void cleanup(); + void construct(); + void initialProperties(); + void initializeProperties(); + void updateProperties(); + +private: + Q3DScatter *m_graph; +}; + +void tst_scatter::initTestCase() +{ +} + +void tst_scatter::cleanupTestCase() +{ +} + +void tst_scatter::init() +{ + m_graph = new Q3DScatter(); +} + +void tst_scatter::cleanup() +{ + delete m_graph; +} + +void tst_scatter::construct() +{ + Q3DScatter *graph = new Q3DScatter(); + QVERIFY(graph); + delete graph; +} + +void tst_scatter::initialProperties() +{ + QVERIFY(m_graph); + QCOMPARE(m_graph->seriesList().length(), 0); + QVERIFY(!m_graph->selectedSeries()); + + // Common properties + QCOMPARE(m_graph->activeTheme()->type(), Q3DTheme::ThemeQt); + QCOMPARE(m_graph->selectionMode(), QAbstract3DGraph::SelectionItem); + QCOMPARE(m_graph->shadowQuality(), QAbstract3DGraph::ShadowQualityMedium); + QCOMPARE(m_graph->measureFps(), false); + QCOMPARE(m_graph->isOrthoProjection(), false); + QCOMPARE(m_graph->selectedElement(), QAbstract3DGraph::ElementNone); + QCOMPARE(m_graph->aspectRatio(), 2.0); + QCOMPARE(m_graph->optimizationHints(), QAbstract3DGraph::OptimizationDefault); + QCOMPARE(m_graph->isPolar(), false); + QCOMPARE(m_graph->radialLabelOffset(), 1.0); + QCOMPARE(m_graph->horizontalAspectRatio(), 0.0); + QCOMPARE(m_graph->isReflection(), false); + QCOMPARE(m_graph->reflectivity(), 0.5); + QCOMPARE(m_graph->locale(), QLocale("C")); + QCOMPARE(m_graph->queriedGraphPosition(), QVector3D(0, 0, 0)); + QCOMPARE(m_graph->margin(), -1.0); +} + +void tst_scatter::initializeProperties() +{ +} + +void tst_scatter::updateProperties() +{ +} + +QTEST_MAIN(tst_scatter) +#include "tst_scatter.moc" diff --git a/tests/auto/cpptest/q3dsurface/q3dsurface.pro b/tests/auto/cpptest/q3dsurface/q3dsurface.pro new file mode 100644 index 00000000..b7a6bf08 --- /dev/null +++ b/tests/auto/cpptest/q3dsurface/q3dsurface.pro @@ -0,0 +1,8 @@ +QT += testlib datavisualization + +TARGET = tst_cpptest +CONFIG += console testcase + +TEMPLATE = app + +SOURCES += tst_surface.cpp diff --git a/tests/auto/cpptest/q3dsurface/tst_surface.cpp b/tests/auto/cpptest/q3dsurface/tst_surface.cpp new file mode 100644 index 00000000..757ece6f --- /dev/null +++ b/tests/auto/cpptest/q3dsurface/tst_surface.cpp @@ -0,0 +1,103 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +#include + +#include + +using namespace QtDataVisualization; + +class tst_surface: public QObject +{ + Q_OBJECT + +private slots: + void initTestCase(); + void cleanupTestCase(); + void init(); + void cleanup(); + void construct(); + void initialProperties(); + void initializeProperties(); + void updateProperties(); + +private: + Q3DSurface *m_graph; +}; + +void tst_surface::initTestCase() +{ +} + +void tst_surface::cleanupTestCase() +{ +} + +void tst_surface::init() +{ + m_graph = new Q3DSurface(); +} + +void tst_surface::cleanup() +{ + delete m_graph; +} + +void tst_surface::construct() +{ + Q3DSurface *graph = new Q3DSurface(); + QVERIFY(graph); + delete graph; +} + +void tst_surface::initialProperties() +{ + QVERIFY(m_graph); + QCOMPARE(m_graph->seriesList().length(), 0); + QVERIFY(!m_graph->selectedSeries()); + QCOMPARE(m_graph->flipHorizontalGrid(), false); + + // Common properties + QCOMPARE(m_graph->activeTheme()->type(), Q3DTheme::ThemeQt); + QCOMPARE(m_graph->selectionMode(), QAbstract3DGraph::SelectionItem); + QCOMPARE(m_graph->shadowQuality(), QAbstract3DGraph::ShadowQualityMedium); + QCOMPARE(m_graph->measureFps(), false); + QCOMPARE(m_graph->isOrthoProjection(), false); + QCOMPARE(m_graph->selectedElement(), QAbstract3DGraph::ElementNone); + QCOMPARE(m_graph->aspectRatio(), 2.0); + QCOMPARE(m_graph->optimizationHints(), QAbstract3DGraph::OptimizationDefault); + QCOMPARE(m_graph->isPolar(), false); + QCOMPARE(m_graph->radialLabelOffset(), 1.0); + QCOMPARE(m_graph->horizontalAspectRatio(), 0.0); + QCOMPARE(m_graph->isReflection(), false); + QCOMPARE(m_graph->reflectivity(), 0.5); + QCOMPARE(m_graph->locale(), QLocale("C")); + QCOMPARE(m_graph->queriedGraphPosition(), QVector3D(0, 0, 0)); + QCOMPARE(m_graph->margin(), -1.0); +} + +void tst_surface::initializeProperties() +{ +} + +void tst_surface::updateProperties() +{ +} + +QTEST_MAIN(tst_surface) +#include "tst_surface.moc" diff --git a/tests/auto/qmltest/tst_qmltest.cpp b/tests/auto/qmltest/tst_qmltest.cpp index 44c1ddea..569d9150 100644 --- a/tests/auto/qmltest/tst_qmltest.cpp +++ b/tests/auto/qmltest/tst_qmltest.cpp @@ -1,36 +1,18 @@ /**************************************************************************** ** -** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com ** -** This file is part of the test suite of the Qt Toolkit. +** This file is part of the QtDataVisualization module. ** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the ** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. +** a written agreement between you and Digia. ** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPLv3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or later as published by the Free -** Software Foundation and appearing in the file LICENSE.GPL included in -** the packaging of this file. Please review the following information to -** ensure the GNU General Public License version 2.0 requirements will be -** met: http://www.gnu.org/licenses/gpl-2.0.html. -** -** $QT_END_LICENSE$ +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com ** ****************************************************************************/ -- cgit v1.2.3 From fd9486b6cbe28a766450e35ad97d845584f56fb1 Mon Sep 17 00:00:00 2001 From: Mika Salmela Date: Fri, 10 Oct 2014 13:09:01 +0300 Subject: Qml performance measurement app Change-Id: I425da013160a12c1d2b3985e04059886d0695474 Reviewed-by: Mika Salmela --- tests/qmlperf/main.cpp | 47 +++++++++ tests/qmlperf/qml/qmlperf/main.qml | 189 ++++++++++++++++++++++++++++++++++++ tests/qmlperf/qml/qmlperf/script.js | 33 +++++++ tests/qmlperf/qmlperf.pro | 12 +++ tests/qmlperf/qmlperf.qrc | 6 ++ tests/tests.pro | 3 +- 6 files changed, 289 insertions(+), 1 deletion(-) create mode 100644 tests/qmlperf/main.cpp create mode 100644 tests/qmlperf/qml/qmlperf/main.qml create mode 100644 tests/qmlperf/qml/qmlperf/script.js create mode 100644 tests/qmlperf/qmlperf.pro create mode 100644 tests/qmlperf/qmlperf.qrc diff --git a/tests/qmlperf/main.cpp b/tests/qmlperf/main.cpp new file mode 100644 index 00000000..7d35b2ed --- /dev/null +++ b/tests/qmlperf/main.cpp @@ -0,0 +1,47 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +#include +#include +#include +#include + +int main(int argc, char *argv[]) +{ + QGuiApplication app(argc, argv); + + QQuickView viewer; + + // The following are needed to make examples run without having to install the module + // in desktop environments. +#ifdef Q_OS_WIN + QString extraImportPath(QStringLiteral("%1/../../../%2")); +#else + QString extraImportPath(QStringLiteral("%1/../../%2")); +#endif + viewer.engine()->addImportPath(extraImportPath.arg(QGuiApplication::applicationDirPath(), + QString::fromLatin1("qml"))); + QObject::connect(viewer.engine(), &QQmlEngine::quit, &viewer, &QWindow::close); + + viewer.setTitle(QStringLiteral("QML Performance")); + viewer.setSource(QUrl("qrc:/qml/qmlperf/main.qml")); + viewer.setResizeMode(QQuickView::SizeRootObjectToView); + viewer.show(); + + return app.exec(); +} diff --git a/tests/qmlperf/qml/qmlperf/main.qml b/tests/qmlperf/qml/qmlperf/main.qml new file mode 100644 index 00000000..35f8df5d --- /dev/null +++ b/tests/qmlperf/qml/qmlperf/main.qml @@ -0,0 +1,189 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +import QtQuick 2.1 +import QtQuick.Layouts 1.0 +import QtQuick.Controls 1.0 +import QtDataVisualization 1.1 +import "script.js" as Script +import "." + +Rectangle { + id: mainview + width: 1280 + height: 1024 + + property var itemCount: 1000.0 + property var addItems: 1000.0 + + Button { + id: changeButton + width: 350 + height: 50 + anchors.left: parent.left + enabled: true + text: "Change" + onClicked: { + console.log("changeButton clicked"); + if (graphView.state == "meshsphere") { + graphView.state = "meshcube" + } else if (graphView.state == "meshcube") { + graphView.state = "meshpyramid" + } else if (graphView.state == "meshpyramid") { + graphView.state = "meshpoint" + } else if (graphView.state == "meshpoint") { + graphView.state = "meshsphere" + } + } + } + + Text { + id: fpsText + text: "Reading" + width: 300 + height: 50 + anchors.left: changeButton.right + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + } + + Button { + id: optimization + width: 300 + height: 50 + anchors.left: fpsText.right + enabled: true + text: scatterPlot.optimizationHints === AbstractGraph3D.OptimizationDefault ? "To Static" : "To Default" + onClicked: { + console.log("Optimization"); + if (scatterPlot.optimizationHints === AbstractGraph3D.OptimizationDefault) { + scatterPlot.optimizationHints = AbstractGraph3D.OptimizationStatic; + optimization.text = "To Default"; + } else { + scatterPlot.optimizationHints = AbstractGraph3D.OptimizationDefault; + optimization.text = "To Static"; + } + } + } + + Button { + id: itemAdd + width: 300 + height: 50 + anchors.left: optimization.right + enabled: true + text: "Add" + onClicked: { + itemCount = itemCount + addItems; + Script.createData(addItems); + } + } + + Item { + id: graphView + width: mainview.width + height: mainview.height + anchors.top: changeButton.bottom + anchors.left: mainview.left + state: "meshsphere" + + ListModel { + id: dataModel + Component.onCompleted: Script.createData(itemCount) + } + + Scatter3D { + id: scatterPlot + width: graphView.width + height: graphView.height + shadowQuality: AbstractGraph3D.ShadowQualityNone + optimizationHints: AbstractGraph3D.OptimizationDefault + scene.activeCamera.yRotation: 45.0 + measureFps: true + onCurrentFpsChanged: { + fpsText.text = itemCount + " : " + scatterPlot.currentFps.toFixed(1); + } + +// theme: Theme3D { +// type: Theme3D.ThemeRetro +// colorStyle: Theme3D.ColorStyleRangeGradient +// baseGradients: customGradient + +// ColorGradient { +// id: customGradient +// ColorGradientStop { position: 1.0; color: "red" } +// ColorGradientStop { position: 0.0; color: "blue" } +// } +// } + + Scatter3DSeries { + id: scatterSeries + mesh: Abstract3DSeries.MeshSphere + ItemModelScatterDataProxy { + itemModel: dataModel + xPosRole: "x" + yPosRole: "y" + zPosRole: "z" + } + } + } + + states: [ + State { + name: "meshsphere" + StateChangeScript { + name: "doSphere" + script: { + console.log("Do the sphere"); + scatterSeries.mesh = Abstract3DSeries.MeshSphere; + } + } + }, + State { + name: "meshcube" + StateChangeScript { + name: "doCube" + script: { + console.log("Do the cube"); + scatterSeries.mesh = Abstract3DSeries.MeshCube; + } + } + }, + State { + name: "meshpyramid" + StateChangeScript { + name: "doPyramid" + script: { + console.log("Do the pyramid"); + scatterSeries.mesh = Abstract3DSeries.MeshPyramid; + } + } + }, + State { + name: "meshpoint" + StateChangeScript { + name: "doPoint" + script: { + console.log("Do the point"); + scatterSeries.mesh = Abstract3DSeries.MeshPoint; + } + } + } + ] + } +} diff --git a/tests/qmlperf/qml/qmlperf/script.js b/tests/qmlperf/qml/qmlperf/script.js new file mode 100644 index 00000000..dc271e8d --- /dev/null +++ b/tests/qmlperf/qml/qmlperf/script.js @@ -0,0 +1,33 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +//function createData(base) { +// for (var z = 0; z < 30; z++) { +// for (var x = 0; x < 30; x++) { +// var angle = (((z - 16) * (x - 16)) / 144.0) * 1.57; +// var y = Math.sin(angle + base); +// dataModel.append({"z": z, "x": x, "y": y}); +// } +// } +//} + +function createData(base) { + for (var i = 0; i < base; i++) { + dataModel.append({"z": Math.random(), "x": Math.random(), "y": Math.random()}); + } +} diff --git a/tests/qmlperf/qmlperf.pro b/tests/qmlperf/qmlperf.pro new file mode 100644 index 00000000..6560f55c --- /dev/null +++ b/tests/qmlperf/qmlperf.pro @@ -0,0 +1,12 @@ +!include( ../tests.pri ) { + error( "Couldn't find the tests.pri file!" ) +} + +# The .cpp file which was generated for your project. Feel free to hack it. +SOURCES += main.cpp + +RESOURCES += qmlperf.qrc + +OTHER_FILES += doc/src/* \ + doc/images/* \ + qml/qmlperf/* diff --git a/tests/qmlperf/qmlperf.qrc b/tests/qmlperf/qmlperf.qrc new file mode 100644 index 00000000..e50815c5 --- /dev/null +++ b/tests/qmlperf/qmlperf.qrc @@ -0,0 +1,6 @@ + + + qml/qmlperf/main.qml + qml/qmlperf/script.js + + diff --git a/tests/tests.pro b/tests/tests.pro index e5364a0e..34ddb5e5 100644 --- a/tests/tests.pro +++ b/tests/tests.pro @@ -19,7 +19,8 @@ SUBDIRS += barstest \ qmlmultitest \ volumetrictest \ qmlvolume \ - auto + auto \ + qmlperf #SUBDIRS += kinectsurface -- cgit v1.2.3 From 9b35362d50200de75e5c685840c00346d37dcf48 Mon Sep 17 00:00:00 2001 From: Mika Salmela Date: Fri, 10 Oct 2014 15:56:47 +0300 Subject: Remove separate depth texture for surface shadows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use the same depth texture for the backwall and the surfaces. No need to any culled texture. Task-number: QTRD-3160 Change-Id: I7a4e8aa61b98d5264a92173174e911e50af8d52a Reviewed-by: Miikka Heikkinen Reviewed-by: Tomi Korpipää --- src/datavisualization/engine/surface3drenderer.cpp | 49 +++------------------- src/datavisualization/engine/surface3drenderer_p.h | 1 - 2 files changed, 6 insertions(+), 44 deletions(-) diff --git a/src/datavisualization/engine/surface3drenderer.cpp b/src/datavisualization/engine/surface3drenderer.cpp index 15df5598..5daded8f 100644 --- a/src/datavisualization/engine/surface3drenderer.cpp +++ b/src/datavisualization/engine/surface3drenderer.cpp @@ -53,7 +53,6 @@ Surface3DRenderer::Surface3DRenderer(Surface3DController *controller) m_scaleX(0.0f), m_scaleY(0.0f), m_scaleZ(0.0f), - m_depthModelTexture(0), m_depthFrameBuffer(0), m_selectionFrameBuffer(0), m_selectionDepthBuffer(0), @@ -93,7 +92,6 @@ Surface3DRenderer::~Surface3DRenderer() m_textureHelper->deleteTexture(&m_noShadowTexture); m_textureHelper->deleteTexture(&m_depthTexture); - m_textureHelper->deleteTexture(&m_depthModelTexture); m_textureHelper->deleteTexture(&m_selectionResultTexture); } delete m_depthShader; @@ -1202,6 +1200,7 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) / (GLfloat)m_primarySubViewport.height(), 3.0f, 100.0f); depthProjectionViewMatrix = depthProjectionMatrix * depthViewMatrix; + // Surface is not closed, so don't cull anything glDisable(GL_CULL_FACE); foreach (SeriesRenderCache *baseCache, m_renderCacheList) { @@ -1231,46 +1230,15 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) } } - glEnable(GL_CULL_FACE); - glCullFace(GL_FRONT); - - Abstract3DRenderer::drawCustomItems(RenderingDepth, m_depthShader, viewMatrix, - projectionViewMatrix, - depthProjectionViewMatrix, m_depthTexture, - m_shadowQualityToShader); - - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, - m_depthModelTexture, 0); - glClear(GL_DEPTH_BUFFER_BIT); - - foreach (SeriesRenderCache *baseCache, m_renderCacheList) { - SurfaceSeriesRenderCache *cache = static_cast(baseCache); - SurfaceObject *object = cache->surfaceObject(); - if (object->indexCount() && cache->surfaceVisible() && cache->isVisible() - && cache->sampleSpace().width() >= 2 && cache->sampleSpace().height() >= 2) { - m_depthShader->setUniformValue(m_depthShader->MVP(), cache->MVPMatrix()); - - // 1st attribute buffer : vertices - glEnableVertexAttribArray(m_depthShader->posAtt()); - glBindBuffer(GL_ARRAY_BUFFER, object->vertexBuf()); - glVertexAttribPointer(m_depthShader->posAtt(), 3, GL_FLOAT, GL_FALSE, 0, - (void *)0); - - // Index buffer - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, object->elementBuf()); - - // Draw the triangles - glDrawElements(GL_TRIANGLES, object->indexCount(), - object->indicesType(), (void *)0); - } - } - // Free buffers glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); glBindBuffer(GL_ARRAY_BUFFER, 0); glDisableVertexAttribArray(m_depthShader->posAtt()); + glEnable(GL_CULL_FACE); + glCullFace(GL_FRONT); + Abstract3DRenderer::drawCustomItems(RenderingDepth, m_depthShader, viewMatrix, projectionViewMatrix, depthProjectionViewMatrix, m_depthTexture, @@ -1453,7 +1421,7 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) // Draw the objects m_drawer->drawObject(shader, cache->surfaceObject(), texture, - m_depthModelTexture); + m_depthTexture); } else #endif { @@ -3025,7 +2993,6 @@ void Surface3DRenderer::initDepthShader() void Surface3DRenderer::updateDepthBuffer() { m_textureHelper->deleteTexture(&m_depthTexture); - m_textureHelper->deleteTexture(&m_depthModelTexture); if (m_primarySubViewport.size().isEmpty()) return; @@ -3034,11 +3001,7 @@ void Surface3DRenderer::updateDepthBuffer() m_depthTexture = m_textureHelper->createDepthTextureFrameBuffer(m_primarySubViewport.size(), m_depthFrameBuffer, m_shadowQualityMultiplier); - if (m_depthTexture) { - m_depthModelTexture = m_textureHelper->createDepthTexture(m_primarySubViewport.size(), - m_shadowQualityMultiplier); - } - if (!m_depthTexture || !m_depthModelTexture) + if (!m_depthTexture) lowerShadowQuality(); } } diff --git a/src/datavisualization/engine/surface3drenderer_p.h b/src/datavisualization/engine/surface3drenderer_p.h index b02bd456..090fe8a9 100644 --- a/src/datavisualization/engine/surface3drenderer_p.h +++ b/src/datavisualization/engine/surface3drenderer_p.h @@ -61,7 +61,6 @@ private: float m_scaleX; float m_scaleY; float m_scaleZ; - GLuint m_depthModelTexture; GLuint m_depthFrameBuffer; GLuint m_selectionFrameBuffer; GLuint m_selectionDepthBuffer; -- cgit v1.2.3 From 8ff6a5d6d89d80707dc07fc96e22160fa1f8e973 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomi=20Korpip=C3=A4=C3=A4?= Date: Mon, 13 Oct 2014 09:58:14 +0300 Subject: Q3DBars, Q3DScatter and Q3DSurface autotests Task-number: QTRD-3368 Change-Id: I86f1d632fe4421c35cc492161c7334602e6f61c0 Change-Id: I86f1d632fe4421c35cc492161c7334602e6f61c0 Reviewed-by: Miikka Heikkinen --- tests/auto/cpptest/q3dbars/tst_bars.cpp | 290 +++++++++++++++++++++++++- tests/auto/cpptest/q3dscatter/tst_scatter.cpp | 132 +++++++++++- tests/auto/cpptest/q3dsurface/tst_surface.cpp | 141 ++++++++++++- 3 files changed, 557 insertions(+), 6 deletions(-) diff --git a/tests/auto/cpptest/q3dbars/tst_bars.cpp b/tests/auto/cpptest/q3dbars/tst_bars.cpp index 5422e7fc..630afac3 100644 --- a/tests/auto/cpptest/q3dbars/tst_bars.cpp +++ b/tests/auto/cpptest/q3dbars/tst_bars.cpp @@ -19,6 +19,9 @@ #include #include +#include +#include +#include using namespace QtDataVisualization; @@ -31,15 +34,44 @@ private slots: void cleanupTestCase(); void init(); void cleanup(); + void construct(); + void initialProperties(); void initializeProperties(); - void updateProperties(); + void invalidProperties(); + + void addSeries(); + void addMultipleSeries(); + void selectSeries(); + void removeSeries(); + void removeMultipleSeries(); + + // The following tests are not required for scatter or surface, as they are handled identically + void addInputHandler(); + void removeInputHandler(); + + void addTheme(); + void removeTheme(); + + void addCustomItem(); + void removeCustomItem(); + + void renderToImage(); private: Q3DBars *m_graph; }; +QBar3DSeries *newSeries() +{ + QBar3DSeries *series = new QBar3DSeries; + QBarDataRow *data = new QBarDataRow; + *data << -1.0f << 3.0f << 7.5f << 5.0f << 2.2f; + series->dataProxy()->addRow(data); + return series; +} + void tst_bars::initTestCase() { } @@ -81,6 +113,7 @@ void tst_bars::initialProperties() QCOMPARE(m_graph->activeTheme()->type(), Q3DTheme::ThemeQt); QCOMPARE(m_graph->selectionMode(), QAbstract3DGraph::SelectionItem); QCOMPARE(m_graph->shadowQuality(), QAbstract3DGraph::ShadowQualityMedium); + QVERIFY(m_graph->scene()); QCOMPARE(m_graph->measureFps(), false); QCOMPARE(m_graph->isOrthoProjection(), false); QCOMPARE(m_graph->selectedElement(), QAbstract3DGraph::ElementNone); @@ -98,10 +131,263 @@ void tst_bars::initialProperties() void tst_bars::initializeProperties() { + QVERIFY(m_graph); + + m_graph->setMultiSeriesUniform(true); + m_graph->setBarThickness(0.2f); + m_graph->setBarSpacing(QSizeF(0.1f, 0.1f)); + m_graph->setBarSpacingRelative(false); + m_graph->setFloorLevel(1.0f); + + QCOMPARE(m_graph->isMultiSeriesUniform(), true); + QCOMPARE(m_graph->barThickness(), 0.2f); + QCOMPARE(m_graph->barSpacing(), QSizeF(0.1f, 0.1f)); + QCOMPARE(m_graph->isBarSpacingRelative(), false); + QCOMPARE(m_graph->floorLevel(), 1.0f); + + Q3DTheme *theme = new Q3DTheme(Q3DTheme::ThemeDigia); + m_graph->setActiveTheme(theme); + m_graph->setSelectionMode(QAbstract3DGraph::SelectionItem | QAbstract3DGraph::SelectionRow | QAbstract3DGraph::SelectionSlice); + m_graph->setShadowQuality(QAbstract3DGraph::ShadowQualitySoftHigh); + QCOMPARE(m_graph->shadowQuality(), QAbstract3DGraph::ShadowQualitySoftHigh); + m_graph->setMeasureFps(true); + m_graph->setOrthoProjection(true); + m_graph->setAspectRatio(1.0); + m_graph->setOptimizationHints(QAbstract3DGraph::OptimizationStatic); + m_graph->setPolar(true); + m_graph->setRadialLabelOffset(0.1f); + m_graph->setHorizontalAspectRatio(1.0); + m_graph->setReflection(true); + m_graph->setReflectivity(0.1); + m_graph->setLocale(QLocale("FI")); + m_graph->setMargin(1.0); + + QCOMPARE(m_graph->activeTheme()->type(), Q3DTheme::ThemeDigia); + QCOMPARE(m_graph->selectionMode(), QAbstract3DGraph::SelectionItem | QAbstract3DGraph::SelectionRow | QAbstract3DGraph::SelectionSlice); + QCOMPARE(m_graph->shadowQuality(), QAbstract3DGraph::ShadowQualityNone); // Ortho disables shadows + QCOMPARE(m_graph->measureFps(), true); + QCOMPARE(m_graph->isOrthoProjection(), true); + QCOMPARE(m_graph->aspectRatio(), 1.0); + QCOMPARE(m_graph->optimizationHints(), QAbstract3DGraph::OptimizationStatic); + QCOMPARE(m_graph->isPolar(), true); + QCOMPARE(m_graph->radialLabelOffset(), 0.1f); + QCOMPARE(m_graph->horizontalAspectRatio(), 1.0); + QCOMPARE(m_graph->isReflection(), true); + QCOMPARE(m_graph->reflectivity(), 0.1); + QCOMPARE(m_graph->locale(), QLocale("FI")); + QCOMPARE(m_graph->margin(), 1.0); } -void tst_bars::updateProperties() +void tst_bars::invalidProperties() { + m_graph->setSelectionMode(QAbstract3DGraph::SelectionColumn | QAbstract3DGraph::SelectionRow | QAbstract3DGraph::SelectionSlice); + m_graph->setAspectRatio(-1.0); + m_graph->setHorizontalAspectRatio(-1.0); + m_graph->setReflectivity(-1.0); + m_graph->setLocale(QLocale("XX")); + + QCOMPARE(m_graph->selectionMode(), QAbstract3DGraph::SelectionItem); + QCOMPARE(m_graph->aspectRatio(), -1.0/*2.0*/); // TODO: Fix once QTRD-3367 is done + QCOMPARE(m_graph->horizontalAspectRatio(), -1.0/*0.0*/); // TODO: Fix once QTRD-3367 is done + QCOMPARE(m_graph->reflectivity(), -1.0/*0.5*/); // TODO: Fix once QTRD-3367 is done + QCOMPARE(m_graph->locale(), QLocale("C")); +} + +void tst_bars::addSeries() +{ + QBar3DSeries *series = newSeries(); + + m_graph->addSeries(series); + + QCOMPARE(m_graph->seriesList().length(), 1); + QVERIFY(!m_graph->selectedSeries()); + QCOMPARE(m_graph->primarySeries(), series); +} + +void tst_bars::addMultipleSeries() +{ + QBar3DSeries *series = newSeries(); + QBar3DSeries *series2 = newSeries(); + QBar3DSeries *series3 = newSeries(); + + m_graph->addSeries(series); + m_graph->addSeries(series2); + m_graph->addSeries(series3); + + QCOMPARE(m_graph->seriesList().length(), 3); + QCOMPARE(m_graph->primarySeries(), series); + + m_graph->setPrimarySeries(series2); + + QCOMPARE(m_graph->primarySeries(), series2); +} + +void tst_bars::selectSeries() +{ + QBar3DSeries *series = newSeries(); + + m_graph->addSeries(series); + m_graph->primarySeries()->setSelectedBar(QPoint(0, 0)); + + QCOMPARE(m_graph->seriesList().length(), 1); + QCOMPARE(m_graph->selectedSeries(), series); + + m_graph->clearSelection(); + QVERIFY(!m_graph->selectedSeries()); +} + +void tst_bars::removeSeries() +{ + QBar3DSeries *series = newSeries(); + + m_graph->addSeries(series); + m_graph->removeSeries(series); + QCOMPARE(m_graph->seriesList().length(), 0); +} + +void tst_bars::removeMultipleSeries() +{ + QBar3DSeries *series = newSeries(); + QBar3DSeries *series2 = newSeries(); + QBar3DSeries *series3 = newSeries(); + + m_graph->addSeries(series); + m_graph->addSeries(series2); + m_graph->addSeries(series3); + + m_graph->primarySeries()->setSelectedBar(QPoint(0, 0)); + QCOMPARE(m_graph->selectedSeries(), series); + + m_graph->removeSeries(series); + QCOMPARE(m_graph->seriesList().length(), 2); + QCOMPARE(m_graph->primarySeries(), series2); + QVERIFY(!m_graph->selectedSeries()); + + m_graph->removeSeries(series2); + QCOMPARE(m_graph->seriesList().length(), 1); + QCOMPARE(m_graph->primarySeries(), series3); + + m_graph->removeSeries(series3); + QCOMPARE(m_graph->seriesList().length(), 0); +} + +// The following tests are not required for scatter or surface, as they are handled identically +void tst_bars::addInputHandler() +{ + Q3DInputHandler *handler = new Q3DInputHandler(); + QTouch3DInputHandler *handler2 = new QTouch3DInputHandler(); + QAbstract3DInputHandler *initialHandler = m_graph->activeInputHandler(); + + m_graph->addInputHandler(handler); + m_graph->addInputHandler(handler2); + + QCOMPARE(m_graph->inputHandlers().length(), 3); // Default, as it is still active, plus added ones + QCOMPARE(m_graph->activeInputHandler(), initialHandler); + m_graph->setActiveInputHandler(handler2); + QCOMPARE(m_graph->activeInputHandler(), handler2); + + m_graph->setActiveInputHandler(NULL); + QVERIFY(!m_graph->activeInputHandler()); + QCOMPARE(m_graph->inputHandlers().length(), 2); +} + +void tst_bars::removeInputHandler() +{ + Q3DInputHandler *handler = new Q3DInputHandler(); + QTouch3DInputHandler *handler2 = new QTouch3DInputHandler(); + + m_graph->addInputHandler(handler); + m_graph->addInputHandler(handler2); + + m_graph->setActiveInputHandler(handler2); + QCOMPARE(m_graph->inputHandlers().length(), 2); // Default handler removed by previous call + QCOMPARE(m_graph->activeInputHandler(), handler2); + m_graph->releaseInputHandler(handler2); + QCOMPARE(m_graph->inputHandlers().length(), 1); + m_graph->releaseInputHandler(handler); + QCOMPARE(m_graph->inputHandlers().length(), 0); + + delete handler2; + delete handler; +} + +void tst_bars::addTheme() +{ + Q3DTheme *theme = new Q3DTheme(Q3DTheme::ThemeDigia); + Q3DTheme *theme2 = new Q3DTheme(); + Q3DTheme *initialTheme = m_graph->activeTheme(); + m_graph->addTheme(theme); + m_graph->addTheme(theme2); + + QCOMPARE(m_graph->themes().length(), 3); // Default, plus added ones + QCOMPARE(m_graph->activeTheme(), initialTheme); + m_graph->setActiveTheme(theme2); + QCOMPARE(m_graph->activeTheme(), theme2); +} + +void tst_bars::removeTheme() +{ + Q3DTheme *theme = new Q3DTheme(Q3DTheme::ThemeDigia); + Q3DTheme *theme2 = new Q3DTheme(); + m_graph->addTheme(theme); + m_graph->addTheme(theme2); + + m_graph->setActiveTheme(theme2); + QCOMPARE(m_graph->activeTheme(), theme2); + m_graph->releaseTheme(theme2); + QCOMPARE(m_graph->themes().length(), 2); + m_graph->releaseTheme(theme); + QCOMPARE(m_graph->themes().length(), 1); // Default theme remains + + delete theme2; + delete theme; +} + +void tst_bars::addCustomItem() +{ + QCustom3DItem *item = new QCustom3DItem(); + QCustom3DItem *item2 = new QCustom3DItem(); + + m_graph->addCustomItem(item); + QCOMPARE(m_graph->customItems().length(), 1); + m_graph->addCustomItem(item2); + QCOMPARE(m_graph->customItems().length(), 2); +} + +void tst_bars::removeCustomItem() +{ + QCustom3DItem *item = new QCustom3DItem(); + QCustom3DItem *item2 = new QCustom3DItem(); + QCustom3DItem *item3 = new QCustom3DItem(); + item3->setPosition(QVector3D(1, 1, 1)); + + m_graph->addCustomItem(item); + m_graph->addCustomItem(item2); + m_graph->addCustomItem(item3); + + m_graph->releaseCustomItem(item); + QCOMPARE(m_graph->customItems().length(), 2); + m_graph->removeCustomItem(item2); + QCOMPARE(m_graph->customItems().length(), 1); + m_graph->addCustomItem(item); + m_graph->removeCustomItemAt(QVector3D(1, 1, 1)); + QCOMPARE(m_graph->customItems().length(), 1); + m_graph->removeCustomItems(); + QCOMPARE(m_graph->customItems().length(), 0); +} + +void tst_bars::renderToImage() +{ + m_graph->addSeries(newSeries()); + + QImage image = m_graph->renderToImage(); + QCOMPARE(image.size(), m_graph->size()); + + image = m_graph->renderToImage(8); + QCOMPARE(image.size(), m_graph->size()); + + image = m_graph->renderToImage(4, QSize(300, 300)); + QCOMPARE(image.size(), QSize(300, 300)); } QTEST_MAIN(tst_bars) diff --git a/tests/auto/cpptest/q3dscatter/tst_scatter.cpp b/tests/auto/cpptest/q3dscatter/tst_scatter.cpp index 2ec0952e..0bbc2122 100644 --- a/tests/auto/cpptest/q3dscatter/tst_scatter.cpp +++ b/tests/auto/cpptest/q3dscatter/tst_scatter.cpp @@ -31,15 +31,32 @@ private slots: void cleanupTestCase(); void init(); void cleanup(); + void construct(); + void initialProperties(); void initializeProperties(); - void updateProperties(); + void invalidProperties(); + + void addSeries(); + void addMultipleSeries(); + void selectSeries(); + void removeSeries(); + void removeMultipleSeries(); private: Q3DScatter *m_graph; }; +QScatter3DSeries *newSeries() +{ + QScatter3DSeries *series = new QScatter3DSeries; + QScatterDataArray data; + data << QVector3D(0.5f, 0.5f, 0.5f) << QVector3D(-0.3f, -0.5f, -0.4f) << QVector3D(0.0f, -0.3f, 0.2f); + series->dataProxy()->addItems(data); + return series; +} + void tst_scatter::initTestCase() { } @@ -75,6 +92,7 @@ void tst_scatter::initialProperties() QCOMPARE(m_graph->activeTheme()->type(), Q3DTheme::ThemeQt); QCOMPARE(m_graph->selectionMode(), QAbstract3DGraph::SelectionItem); QCOMPARE(m_graph->shadowQuality(), QAbstract3DGraph::ShadowQualityMedium); + QVERIFY(m_graph->scene()); QCOMPARE(m_graph->measureFps(), false); QCOMPARE(m_graph->isOrthoProjection(), false); QCOMPARE(m_graph->selectedElement(), QAbstract3DGraph::ElementNone); @@ -92,10 +110,120 @@ void tst_scatter::initialProperties() void tst_scatter::initializeProperties() { + Q3DTheme *theme = new Q3DTheme(Q3DTheme::ThemeDigia); + m_graph->setActiveTheme(theme); + m_graph->setSelectionMode(QAbstract3DGraph::SelectionNone); + m_graph->setShadowQuality(QAbstract3DGraph::ShadowQualitySoftHigh); + QCOMPARE(m_graph->shadowQuality(), QAbstract3DGraph::ShadowQualitySoftHigh); + m_graph->setMeasureFps(true); + m_graph->setOrthoProjection(true); + m_graph->setAspectRatio(1.0); + m_graph->setOptimizationHints(QAbstract3DGraph::OptimizationStatic); + m_graph->setPolar(true); + m_graph->setRadialLabelOffset(0.1f); + m_graph->setHorizontalAspectRatio(1.0); + m_graph->setReflection(true); + m_graph->setReflectivity(0.1); + m_graph->setLocale(QLocale("FI")); + m_graph->setMargin(1.0); + + QCOMPARE(m_graph->activeTheme()->type(), Q3DTheme::ThemeDigia); + QCOMPARE(m_graph->selectionMode(), QAbstract3DGraph::SelectionNone); + QCOMPARE(m_graph->shadowQuality(), QAbstract3DGraph::ShadowQualityNone); // Ortho disables shadows + QCOMPARE(m_graph->measureFps(), true); + QCOMPARE(m_graph->isOrthoProjection(), true); + QCOMPARE(m_graph->aspectRatio(), 1.0); + QCOMPARE(m_graph->optimizationHints(), QAbstract3DGraph::OptimizationStatic); + QCOMPARE(m_graph->isPolar(), true); + QCOMPARE(m_graph->radialLabelOffset(), 0.1f); + QCOMPARE(m_graph->horizontalAspectRatio(), 1.0); + QCOMPARE(m_graph->isReflection(), true); + QCOMPARE(m_graph->reflectivity(), 0.1); + QCOMPARE(m_graph->locale(), QLocale("FI")); + QCOMPARE(m_graph->margin(), 1.0); } -void tst_scatter::updateProperties() +void tst_scatter::invalidProperties() { + m_graph->setSelectionMode(QAbstract3DGraph::SelectionColumn | QAbstract3DGraph::SelectionRow | QAbstract3DGraph::SelectionSlice); + m_graph->setAspectRatio(-1.0); + m_graph->setHorizontalAspectRatio(-1.0); + m_graph->setReflectivity(-1.0); + m_graph->setLocale(QLocale("XX")); + + QCOMPARE(m_graph->selectionMode(), QAbstract3DGraph::SelectionItem); + QCOMPARE(m_graph->aspectRatio(), -1.0/*2.0*/); // TODO: Fix once QTRD-3367 is done + QCOMPARE(m_graph->horizontalAspectRatio(), -1.0/*0.0*/); // TODO: Fix once QTRD-3367 is done + QCOMPARE(m_graph->reflectivity(), -1.0/*0.5*/); // TODO: Fix once QTRD-3367 is done + QCOMPARE(m_graph->locale(), QLocale("C")); +} + +void tst_scatter::addSeries() +{ + m_graph->addSeries(newSeries()); + + QCOMPARE(m_graph->seriesList().length(), 1); + QVERIFY(!m_graph->selectedSeries()); +} + +void tst_scatter::addMultipleSeries() +{ + QScatter3DSeries *series = newSeries(); + QScatter3DSeries *series2 = newSeries(); + QScatter3DSeries *series3 = newSeries(); + + m_graph->addSeries(series); + m_graph->addSeries(series2); + m_graph->addSeries(series3); + + QCOMPARE(m_graph->seriesList().length(), 3); +} + +void tst_scatter::selectSeries() +{ + QScatter3DSeries *series = newSeries(); + + m_graph->addSeries(series); + m_graph->seriesList()[0]->setSelectedItem(1); + + QCOMPARE(m_graph->seriesList().length(), 1); + QCOMPARE(m_graph->selectedSeries(), series); + + m_graph->clearSelection(); + QVERIFY(!m_graph->selectedSeries()); +} + +void tst_scatter::removeSeries() +{ + QScatter3DSeries *series = newSeries(); + + m_graph->addSeries(series); + m_graph->removeSeries(series); + QCOMPARE(m_graph->seriesList().length(), 0); +} + +void tst_scatter::removeMultipleSeries() +{ + QScatter3DSeries *series = newSeries(); + QScatter3DSeries *series2 = newSeries(); + QScatter3DSeries *series3 = newSeries(); + + m_graph->addSeries(series); + m_graph->addSeries(series2); + m_graph->addSeries(series3); + + m_graph->seriesList()[0]->setSelectedItem(1); + QCOMPARE(m_graph->selectedSeries(), series); + + m_graph->removeSeries(series); + QCOMPARE(m_graph->seriesList().length(), 2); + QVERIFY(!m_graph->selectedSeries()); + + m_graph->removeSeries(series2); + QCOMPARE(m_graph->seriesList().length(), 1); + + m_graph->removeSeries(series3); + QCOMPARE(m_graph->seriesList().length(), 0); } QTEST_MAIN(tst_scatter) diff --git a/tests/auto/cpptest/q3dsurface/tst_surface.cpp b/tests/auto/cpptest/q3dsurface/tst_surface.cpp index 757ece6f..7b2d3ebb 100644 --- a/tests/auto/cpptest/q3dsurface/tst_surface.cpp +++ b/tests/auto/cpptest/q3dsurface/tst_surface.cpp @@ -31,15 +31,37 @@ private slots: void cleanupTestCase(); void init(); void cleanup(); + void construct(); + void initialProperties(); void initializeProperties(); - void updateProperties(); + void invalidProperties(); + + void addSeries(); + void addMultipleSeries(); + void selectSeries(); + void removeSeries(); + void removeMultipleSeries(); private: Q3DSurface *m_graph; }; +QSurface3DSeries *newSeries() +{ + QSurface3DSeries *series = new QSurface3DSeries; + QSurfaceDataArray *data = new QSurfaceDataArray; + QSurfaceDataRow *dataRow1 = new QSurfaceDataRow; + QSurfaceDataRow *dataRow2 = new QSurfaceDataRow; + *dataRow1 << QVector3D(0.0f, 0.1f, 0.5f) << QVector3D(1.0f, 0.5f, 0.5f); + *dataRow2 << QVector3D(0.0f, 1.8f, 1.0f) << QVector3D(1.0f, 1.2f, 1.0f); + *data << dataRow1 << dataRow2; + series->dataProxy()->resetArray(data); + + return series; +} + void tst_surface::initTestCase() { } @@ -76,6 +98,7 @@ void tst_surface::initialProperties() QCOMPARE(m_graph->activeTheme()->type(), Q3DTheme::ThemeQt); QCOMPARE(m_graph->selectionMode(), QAbstract3DGraph::SelectionItem); QCOMPARE(m_graph->shadowQuality(), QAbstract3DGraph::ShadowQualityMedium); + QVERIFY(m_graph->scene()); QCOMPARE(m_graph->measureFps(), false); QCOMPARE(m_graph->isOrthoProjection(), false); QCOMPARE(m_graph->selectedElement(), QAbstract3DGraph::ElementNone); @@ -93,10 +116,124 @@ void tst_surface::initialProperties() void tst_surface::initializeProperties() { + m_graph->setFlipHorizontalGrid(true); + + QCOMPARE(m_graph->flipHorizontalGrid(), true); + + Q3DTheme *theme = new Q3DTheme(Q3DTheme::ThemeDigia); + m_graph->setActiveTheme(theme); + m_graph->setSelectionMode(QAbstract3DGraph::SelectionItem | QAbstract3DGraph::SelectionRow | QAbstract3DGraph::SelectionSlice); + m_graph->setShadowQuality(QAbstract3DGraph::ShadowQualitySoftHigh); + QCOMPARE(m_graph->shadowQuality(), QAbstract3DGraph::ShadowQualitySoftHigh); + m_graph->setMeasureFps(true); + m_graph->setOrthoProjection(true); + m_graph->setAspectRatio(1.0); + m_graph->setOptimizationHints(QAbstract3DGraph::OptimizationStatic); + m_graph->setPolar(true); + m_graph->setRadialLabelOffset(0.1f); + m_graph->setHorizontalAspectRatio(1.0); + m_graph->setReflection(true); + m_graph->setReflectivity(0.1); + m_graph->setLocale(QLocale("FI")); + m_graph->setMargin(1.0); + + QCOMPARE(m_graph->activeTheme()->type(), Q3DTheme::ThemeDigia); + QCOMPARE(m_graph->selectionMode(), QAbstract3DGraph::SelectionItem | QAbstract3DGraph::SelectionRow | QAbstract3DGraph::SelectionSlice); + QCOMPARE(m_graph->shadowQuality(), QAbstract3DGraph::ShadowQualityNone); // Ortho disables shadows + QCOMPARE(m_graph->measureFps(), true); + QCOMPARE(m_graph->isOrthoProjection(), true); + QCOMPARE(m_graph->aspectRatio(), 1.0); + QCOMPARE(m_graph->optimizationHints(), QAbstract3DGraph::OptimizationStatic); + QCOMPARE(m_graph->isPolar(), true); + QCOMPARE(m_graph->radialLabelOffset(), 0.1f); + QCOMPARE(m_graph->horizontalAspectRatio(), 1.0); + QCOMPARE(m_graph->isReflection(), true); + QCOMPARE(m_graph->reflectivity(), 0.1); + QCOMPARE(m_graph->locale(), QLocale("FI")); + QCOMPARE(m_graph->margin(), 1.0); } -void tst_surface::updateProperties() +void tst_surface::invalidProperties() { + m_graph->setSelectionMode(QAbstract3DGraph::SelectionColumn | QAbstract3DGraph::SelectionRow | QAbstract3DGraph::SelectionSlice); + m_graph->setAspectRatio(-1.0); + m_graph->setHorizontalAspectRatio(-1.0); + m_graph->setReflectivity(-1.0); + m_graph->setLocale(QLocale("XX")); + + QCOMPARE(m_graph->selectionMode(), QAbstract3DGraph::SelectionItem); + QCOMPARE(m_graph->aspectRatio(), -1.0/*2.0*/); // TODO: Fix once QTRD-3367 is done + QCOMPARE(m_graph->horizontalAspectRatio(), -1.0/*0.0*/); // TODO: Fix once QTRD-3367 is done + QCOMPARE(m_graph->reflectivity(), -1.0/*0.5*/); // TODO: Fix once QTRD-3367 is done + QCOMPARE(m_graph->locale(), QLocale("C")); +} + +void tst_surface::addSeries() +{ + m_graph->addSeries(newSeries()); + + QCOMPARE(m_graph->seriesList().length(), 1); + QVERIFY(!m_graph->selectedSeries()); +} + +void tst_surface::addMultipleSeries() +{ + QSurface3DSeries *series = newSeries(); + QSurface3DSeries *series2 = newSeries(); + QSurface3DSeries *series3 = newSeries(); + + m_graph->addSeries(series); + m_graph->addSeries(series2); + m_graph->addSeries(series3); + + QCOMPARE(m_graph->seriesList().length(), 3); +} + +void tst_surface::selectSeries() +{ + QSurface3DSeries *series = newSeries(); + + m_graph->addSeries(series); + m_graph->seriesList()[0]->setSelectedPoint(QPoint(0, 0)); + + QCOMPARE(m_graph->seriesList().length(), 1); + QCOMPARE(m_graph->selectedSeries(), series); + + m_graph->clearSelection(); + QVERIFY(!m_graph->selectedSeries()); +} + +void tst_surface::removeSeries() +{ + QSurface3DSeries *series = newSeries(); + + m_graph->addSeries(series); + m_graph->removeSeries(series); + QCOMPARE(m_graph->seriesList().length(), 0); +} + +void tst_surface::removeMultipleSeries() +{ + QSurface3DSeries *series = newSeries(); + QSurface3DSeries *series2 = newSeries(); + QSurface3DSeries *series3 = newSeries(); + + m_graph->addSeries(series); + m_graph->addSeries(series2); + m_graph->addSeries(series3); + + m_graph->seriesList()[0]->setSelectedPoint(QPoint(0, 0)); + QCOMPARE(m_graph->selectedSeries(), series); + + m_graph->removeSeries(series); + QCOMPARE(m_graph->seriesList().length(), 2); + QVERIFY(!m_graph->selectedSeries()); + + m_graph->removeSeries(series2); + QCOMPARE(m_graph->seriesList().length(), 1); + + m_graph->removeSeries(series3); + QCOMPARE(m_graph->seriesList().length(), 0); } QTEST_MAIN(tst_surface) -- cgit v1.2.3 From 732c9f39f08d7867c8e127c84d319ae514d682fd Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Mon, 13 Oct 2014 10:21:20 +0300 Subject: Fix context cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Context was not getting properly cleaned up in cases where render thread stopped before the cleanup took place. Now we ensure that required cleanup is done before context thread terminates. Change-Id: I1489914dafec928eebb69bac737f6f858ff49432 Reviewed-by: Tomi Korpipää --- .../engine/abstract3dcontroller.cpp | 9 +- .../engine/abstract3dcontroller_p.h | 3 +- .../engine/abstract3drenderer.cpp | 42 +++++++- .../engine/abstract3drenderer_p.h | 9 ++ src/datavisualization/engine/bars3drenderer.cpp | 2 + src/datavisualization/engine/scatter3drenderer.cpp | 2 + src/datavisualization/engine/surface3drenderer.cpp | 25 ++--- src/datavisualizationqml2/abstractdeclarative.cpp | 55 ++++++---- src/datavisualizationqml2/abstractdeclarative_p.h | 1 + tests/qmlmultiwindow/qml/qmlmultiwindow/main.qml | 120 ++++++++++----------- 10 files changed, 169 insertions(+), 99 deletions(-) diff --git a/src/datavisualization/engine/abstract3dcontroller.cpp b/src/datavisualization/engine/abstract3dcontroller.cpp index f8a1a813..37d7c08b 100644 --- a/src/datavisualization/engine/abstract3dcontroller.cpp +++ b/src/datavisualization/engine/abstract3dcontroller.cpp @@ -100,7 +100,7 @@ Abstract3DController::~Abstract3DController() void Abstract3DController::destroyRenderer() { // Renderer can be in another thread, don't delete it directly in that case - if (m_renderer && m_renderer->thread() != QThread::currentThread()) + if (m_renderer && m_renderer->thread() && m_renderer->thread() != this->thread()) m_renderer->deleteLater(); else delete m_renderer; @@ -114,6 +114,13 @@ void Abstract3DController::destroyRenderer() void Abstract3DController::setRenderer(Abstract3DRenderer *renderer) { m_renderer = renderer; + + // If renderer is created in different thread than controller, make sure renderer gets + // destroyed before the render thread finishes. + if (renderer->thread() != this->thread()) { + QObject::connect(renderer->thread(), &QThread::finished, this, + &Abstract3DController::destroyRenderer, Qt::DirectConnection); + } } void Abstract3DController::addSeries(QAbstract3DSeries *series) diff --git a/src/datavisualization/engine/abstract3dcontroller_p.h b/src/datavisualization/engine/abstract3dcontroller_p.h index 4d7b36a3..5bb3d863 100644 --- a/src/datavisualization/engine/abstract3dcontroller_p.h +++ b/src/datavisualization/engine/abstract3dcontroller_p.h @@ -218,7 +218,6 @@ public: virtual ~Abstract3DController(); inline bool isInitialized() { return (m_renderer != 0); } - virtual void destroyRenderer(); virtual void synchDataToRenderer(); virtual void render(const GLuint defaultFboHandle = 0); virtual void initializeOpenGL() = 0; @@ -348,6 +347,8 @@ public: void markSeriesItemLabelsDirty(); public slots: + void destroyRenderer(); + void handleAxisTitleChanged(const QString &title); void handleAxisLabelsChanged(); void handleAxisRangeChanged(float min, float max); diff --git a/src/datavisualization/engine/abstract3drenderer.cpp b/src/datavisualization/engine/abstract3drenderer.cpp index c47e2b29..d7bad300 100644 --- a/src/datavisualization/engine/abstract3drenderer.cpp +++ b/src/datavisualization/engine/abstract3drenderer.cpp @@ -28,6 +28,8 @@ #include "scatter3drenderer_p.h" #include +#include +#include QT_BEGIN_NAMESPACE_DATAVISUALIZATION @@ -101,7 +103,11 @@ Abstract3DRenderer::Abstract3DRenderer(Abstract3DController *controller) m_scaleZWithBackground(0.0f), m_oldCameraTarget(QVector3D(2000.0f, 2000.0f, 2000.0f)), // Just random invalid target m_reflectionEnabled(false), - m_reflectivity(0.5) + m_reflectivity(0.5), + m_context(0), + m_currentContextAtDelete(0), + m_currentSurfaceAtDelete(0), + m_dummySurfaceAtDelete(0) { QObject::connect(m_drawer, &Drawer::drawerChanged, this, &Abstract3DRenderer::updateTextures); QObject::connect(this, &Abstract3DRenderer::needRender, controller, @@ -151,10 +157,14 @@ Abstract3DRenderer::~Abstract3DRenderer() delete m_textureHelper; } + + restoreContextAfterDelete(); } void Abstract3DRenderer::initializeOpenGL() { + m_context = QOpenGLContext::currentContext(); + // Set OpenGL features glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LESS); @@ -1772,6 +1782,36 @@ void Abstract3DRenderer::queriedGraphPosition(const QMatrix4x4 &projectionViewMa m_graphPositionQueryPending = false; } +void Abstract3DRenderer::fixContextBeforeDelete() +{ + m_currentContextAtDelete = QOpenGLContext::currentContext(); + if (m_currentContextAtDelete) + m_currentSurfaceAtDelete = m_currentContextAtDelete->surface(); + if (!m_context.isNull() && m_context.data() != m_currentContextAtDelete + && QThread::currentThread() == this->thread()) { + m_dummySurfaceAtDelete = new QWindow(); + m_dummySurfaceAtDelete->setSurfaceType(QWindow::OpenGLSurface); + m_dummySurfaceAtDelete->setFormat(m_context->format()); + m_dummySurfaceAtDelete->create(); + + m_context->makeCurrent(m_dummySurfaceAtDelete); + } +} + +void Abstract3DRenderer::restoreContextAfterDelete() +{ + if (m_currentContextAtDelete && m_currentSurfaceAtDelete + && m_context.data() != m_currentContextAtDelete) { + m_currentContextAtDelete->makeCurrent(m_currentSurfaceAtDelete); + } else if (m_dummySurfaceAtDelete) { + m_context->doneCurrent(); + } + delete m_dummySurfaceAtDelete; + m_currentContextAtDelete = 0; + m_currentSurfaceAtDelete = 0; + m_dummySurfaceAtDelete = 0; +} + void Abstract3DRenderer::calculatePolarXZ(const QVector3D &dataPos, float &x, float &z) const { // x is angular, z is radial diff --git a/src/datavisualization/engine/abstract3drenderer_p.h b/src/datavisualization/engine/abstract3drenderer_p.h index d8ca7696..1857278c 100644 --- a/src/datavisualization/engine/abstract3drenderer_p.h +++ b/src/datavisualization/engine/abstract3drenderer_p.h @@ -37,6 +37,8 @@ #include "seriesrendercache_p.h" #include "customrenderitem_p.h" +class QSurface; + QT_BEGIN_NAMESPACE_DATAVISUALIZATION class TextureHelper; @@ -229,6 +231,9 @@ protected: void queriedGraphPosition(const QMatrix4x4 &projectionViewMatrix, const QVector3D &scaling, GLuint defaultFboHandle); + void fixContextBeforeDelete(); + void restoreContextAfterDelete(); + bool m_hasNegativeValues; Q3DTheme *m_cachedTheme; Drawer *m_drawer; @@ -318,6 +323,10 @@ protected: qreal m_reflectivity; QLocale m_locale; + QPointer m_context; // Not owned + QOpenGLContext *m_currentContextAtDelete; // Not owned + QSurface *m_currentSurfaceAtDelete; // Not owned + QWindow *m_dummySurfaceAtDelete; private: friend class Abstract3DController; diff --git a/src/datavisualization/engine/bars3drenderer.cpp b/src/datavisualization/engine/bars3drenderer.cpp index e18e4fa5..07bb5f9a 100644 --- a/src/datavisualization/engine/bars3drenderer.cpp +++ b/src/datavisualization/engine/bars3drenderer.cpp @@ -90,6 +90,8 @@ Bars3DRenderer::Bars3DRenderer(Bars3DController *controller) Bars3DRenderer::~Bars3DRenderer() { + fixContextBeforeDelete(); + if (QOpenGLContext::currentContext()) { m_textureHelper->glDeleteFramebuffers(1, &m_selectionFrameBuffer); m_textureHelper->glDeleteRenderbuffers(1, &m_selectionDepthBuffer); diff --git a/src/datavisualization/engine/scatter3drenderer.cpp b/src/datavisualization/engine/scatter3drenderer.cpp index f5d0793f..8ccd0824 100644 --- a/src/datavisualization/engine/scatter3drenderer.cpp +++ b/src/datavisualization/engine/scatter3drenderer.cpp @@ -79,6 +79,8 @@ Scatter3DRenderer::Scatter3DRenderer(Scatter3DController *controller) Scatter3DRenderer::~Scatter3DRenderer() { + fixContextBeforeDelete(); + if (QOpenGLContext::currentContext()) { m_textureHelper->glDeleteFramebuffers(1, &m_selectionFrameBuffer); m_textureHelper->glDeleteRenderbuffers(1, &m_selectionDepthBuffer); diff --git a/src/datavisualization/engine/surface3drenderer.cpp b/src/datavisualization/engine/surface3drenderer.cpp index 5daded8f..efecb0e1 100644 --- a/src/datavisualization/engine/surface3drenderer.cpp +++ b/src/datavisualization/engine/surface3drenderer.cpp @@ -85,6 +85,8 @@ Surface3DRenderer::Surface3DRenderer(Surface3DController *controller) Surface3DRenderer::~Surface3DRenderer() { + fixContextBeforeDelete(); + if (QOpenGLContext::currentContext()) { m_textureHelper->glDeleteFramebuffers(1, &m_depthFrameBuffer); m_textureHelper->glDeleteRenderbuffers(1, &m_selectionDepthBuffer); @@ -2879,19 +2881,12 @@ void Surface3DRenderer::initShaders(const QString &vertexShader, const QString & Q_UNUSED(vertexShader); Q_UNUSED(fragmentShader); - // draw the shader for the surface according to smooth status, shadow and uniform color - if (m_surfaceFlatShader) - delete m_surfaceFlatShader; - if (m_surfaceSmoothShader) - delete m_surfaceSmoothShader; - if (m_surfaceTexturedSmoothShader) - delete m_surfaceTexturedSmoothShader; - if (m_surfaceTexturedFlatShader) - delete m_surfaceTexturedFlatShader; - if (m_surfaceSliceFlatShader) - delete m_surfaceSliceFlatShader; - if (m_surfaceSliceSmoothShader) - delete m_surfaceSliceSmoothShader; + delete m_surfaceFlatShader; + delete m_surfaceSmoothShader; + delete m_surfaceTexturedSmoothShader; + delete m_surfaceTexturedFlatShader; + delete m_surfaceSliceFlatShader; + delete m_surfaceSliceSmoothShader; #if !defined(QT_OPENGL_ES_2) if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { @@ -2924,6 +2919,7 @@ void Surface3DRenderer::initShaders(const QString &vertexShader, const QString & } else { m_surfaceFlatShader = 0; m_surfaceSliceFlatShader = 0; + m_surfaceTexturedFlatShader = 0; } #else m_surfaceSmoothShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertex"), @@ -2983,8 +2979,7 @@ void Surface3DRenderer::initSurfaceShaders() #if !defined(QT_OPENGL_ES_2) void Surface3DRenderer::initDepthShader() { - if (m_depthShader) - delete m_depthShader; + delete m_depthShader; m_depthShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexDepth"), QStringLiteral(":/shaders/fragmentDepth")); m_depthShader->initialize(); diff --git a/src/datavisualizationqml2/abstractdeclarative.cpp b/src/datavisualizationqml2/abstractdeclarative.cpp index 6c6cdb90..7e8de95c 100644 --- a/src/datavisualizationqml2/abstractdeclarative.cpp +++ b/src/datavisualizationqml2/abstractdeclarative.cpp @@ -62,24 +62,7 @@ AbstractDeclarative::AbstractDeclarative(QQuickItem *parent) : AbstractDeclarative::~AbstractDeclarative() { -#ifdef USE_SHARED_CONTEXT - // Context can be in another thread, don't delete it directly in that case - if (m_contextThread && m_contextThread != m_mainThread) { - if (m_context) - m_context->deleteLater(); - m_context = 0; - } else { - delete m_context; - } -#else - if (m_contextThread && m_contextThread != m_mainThread) { - if (m_stateStore) - m_stateStore->deleteLater(); - m_stateStore = 0; - } else { - delete m_stateStore; - } -#endif + destroyContext(); disconnect(this, 0, this, 0); checkWindowList(0); @@ -363,7 +346,6 @@ void AbstractDeclarative::activateOpenGLContext(QQuickWindow *window) m_contextThread = QThread::currentThread(); m_contextWindow = window; m_qtContext = currentContext; - m_context = new QOpenGLContext(); m_context->setFormat(m_qtContext->format()); m_context->setShareContext(m_qtContext); @@ -371,6 +353,10 @@ void AbstractDeclarative::activateOpenGLContext(QQuickWindow *window) m_context->makeCurrent(window); m_controller->initializeOpenGL(); + + // Make sure context gets deleted. + QObject::connect(m_contextThread, &QThread::finished, this, + &AbstractDeclarative::destroyContext, Qt::DirectConnection); } else { m_context->makeCurrent(window); } @@ -392,6 +378,10 @@ void AbstractDeclarative::activateOpenGLContext(QQuickWindow *window) m_stateStore->storeGLState(); m_controller->initializeOpenGL(); + + // Make sure state store gets deleted. + QObject::connect(m_contextThread, &QThread::finished, this, + &AbstractDeclarative::destroyContext, Qt::DirectConnection); } else { m_stateStore->storeGLState(); } @@ -827,4 +817,31 @@ void AbstractDeclarative::windowDestroyed(QObject *obj) windowClearList.remove(win); } +void AbstractDeclarative::destroyContext() +{ +#ifdef USE_SHARED_CONTEXT + // Context can be in another thread, don't delete it directly in that case + if (m_contextThread && m_contextThread != m_mainThread) { + if (m_context) + m_context->deleteLater(); + } else { + delete m_context; + } + m_context = 0; +#else + if (m_contextThread && m_contextThread != m_mainThread) { + if (m_stateStore) + m_stateStore->deleteLater(); + } else { + delete m_stateStore; + } + m_stateStore = 0; +#endif + if (m_contextThread) { + QObject::disconnect(m_contextThread, &QThread::finished, this, + &AbstractDeclarative::destroyContext); + m_contextThread = 0; + } +} + QT_END_NAMESPACE_DATAVISUALIZATION diff --git a/src/datavisualizationqml2/abstractdeclarative_p.h b/src/datavisualizationqml2/abstractdeclarative_p.h index 04db21f4..0c32a4a5 100644 --- a/src/datavisualizationqml2/abstractdeclarative_p.h +++ b/src/datavisualizationqml2/abstractdeclarative_p.h @@ -229,6 +229,7 @@ public slots: virtual void handleAxisYChanged(QAbstract3DAxis *axis) = 0; virtual void handleAxisZChanged(QAbstract3DAxis *axis) = 0; void windowDestroyed(QObject *obj); + void destroyContext(); protected: virtual void mouseDoubleClickEvent(QMouseEvent *event); diff --git a/tests/qmlmultiwindow/qml/qmlmultiwindow/main.qml b/tests/qmlmultiwindow/qml/qmlmultiwindow/main.qml index 7dfe0bec..57d62019 100644 --- a/tests/qmlmultiwindow/qml/qmlmultiwindow/main.qml +++ b/tests/qmlmultiwindow/qml/qmlmultiwindow/main.qml @@ -31,19 +31,51 @@ Rectangle { id: data } - Window { - id: firstWindow - x: 100 - y: 100 - width: 500 - height: 500 - visible: true - Rectangle { - id: firstRect - color: "red" - anchors.fill: parent - } - } + property QtObject surfaceWindowObject; + property string surfaceWindowStr: + "\n + import QtQuick 2.1\n + import QtQuick.Window 2.1\n + import QtQuick.Layouts 1.0\n + import QtDataVisualization 1.0\n + import \".\"\n + Window {\n + Data {\n + id: data\n + }\n + id: firstWindow\n + x: 100\n + y: 100\n + width: 500\n + height: 500\n + visible: true\n + Rectangle {\n + id: firstRect\n + color: \"red\"\n + anchors.fill: parent\n + Surface3D {\n + id: surfaceGraph\n + anchors.fill: parent\n + anchors.margins: parent.border.width\n + theme: Theme3D {\n + type: Theme3D.ThemePrimaryColors\n + font.pointSize: 60\n + }\n + scene.activeCamera.cameraPreset: Camera3D.CameraPresetIsometricLeftHigh\n + Surface3DSeries {\n + itemLabelFormat: \"Pop density at (@xLabel N, @zLabel E): @yLabel\"\n + ItemModelSurfaceDataProxy {\n + itemModel: data.myData\n + rowRole: \"row\"\n + columnRole: \"col\"\n + xPosRole: \"latitude\"\n + zPosRole: \"longitude\"\n + yPosRole: \"pop_density\"\n + }\n + }\n + }\n + }\n + }" Window { id: secondWindow @@ -59,19 +91,6 @@ Rectangle { } } - states: [ - State { - name: "firstWindow" - ParentChange { target: surfaceGraph; parent: firstRect; x: 0; y: 0 } - }, - State { - name: "secondWindow" - ParentChange { target: surfaceGraph; parent: secondRect; x: 0; y: 0 } - } - ] - - state: "firstWindow" - //! [0] GridLayout { id: gridLayout @@ -86,32 +105,18 @@ Rectangle { Rectangle { Layout.fillHeight: true Layout.fillWidth: true - border.color: surfaceGraph.theme.gridLineColor border.width: 2 + } - Surface3D { - id: surfaceGraph - anchors.fill: parent - anchors.margins: parent.border.width - theme: Theme3D { - type: Theme3D.ThemePrimaryColors - font.pointSize: 60 - } - scene.activeCamera.cameraPreset: Camera3D.CameraPresetIsometricLeftHigh - - Surface3DSeries { - itemLabelFormat: "Pop density at (@xLabel N, @zLabel E): @yLabel" - ItemModelSurfaceDataProxy { - itemModel: data.myData - // The surface data points are not neatly lined up in rows and columns, - // so we define explicit row and column roles. - rowRole: "row" - columnRole: "col" - xPosRole: "latitude" - zPosRole: "longitude" - yPosRole: "pop_density" - } - } + Timer { + id: windowToggleTimer + interval: 1000 + running: false + repeat: false + onTriggered: { + if (surfaceWindowObject != null) + surfaceWindowObject.destroy() + surfaceWindowObject = Qt.createQmlObject(surfaceWindowStr, mainView) } } @@ -131,13 +136,10 @@ Rectangle { Layout.minimumWidth: parent.width / 2 Layout.fillHeight: true Layout.fillWidth: true - text: "Move graph between windows" + text: "(re)construct surface window in a loop" onClicked: { - if (mainView.state === "firstWindow") { - mainView.state = "secondWindow" - } else { - mainView.state = "firstWindow" - } + windowToggleTimer.running = true + windowToggleTimer.repeat = true } } @@ -231,14 +233,11 @@ Rectangle { function clearSelections() { barGraph.clearSelection() scatterGraph.clearSelection() - surfaceGraph.clearSelection() } function resetCameras() { - surfaceGraph.scene.activeCamera.cameraPreset = Camera3D.CameraPresetIsometricLeftHigh scatterGraph.scene.activeCamera.cameraPreset = Camera3D.CameraPresetIsometricLeftHigh barGraph.scene.activeCamera.cameraPreset = Camera3D.CameraPresetIsometricLeftHigh - surfaceGraph.scene.activeCamera.zoomLevel = 100.0 scatterGraph.scene.activeCamera.zoomLevel = 100.0 barGraph.scene.activeCamera.zoomLevel = 100.0 } @@ -246,12 +245,9 @@ Rectangle { function toggleMeshStyle() { if (barGraph.seriesList[0].meshSmooth === true) { barGraph.seriesList[0].meshSmooth = false - if (surfaceGraph.seriesList[0].flatShadingSupported) - surfaceGraph.seriesList[0].flatShadingEnabled = true scatterGraph.seriesList[0].meshSmooth = false } else { barGraph.seriesList[0].meshSmooth = true - surfaceGraph.seriesList[0].flatShadingEnabled = false scatterGraph.seriesList[0].meshSmooth = true } } -- cgit v1.2.3 From 631d9bead3aac19bcc5bdbd79df54e7aa216ecff Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Mon, 13 Oct 2014 13:43:15 +0300 Subject: Reset camera target when setting a preset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I3a1595530a976b73144109cf2eef72a188831a10 Reviewed-by: Tomi Korpipää --- src/datavisualization/engine/q3dcamera.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/datavisualization/engine/q3dcamera.cpp b/src/datavisualization/engine/q3dcamera.cpp index 9c3e4c08..273b8415 100644 --- a/src/datavisualization/engine/q3dcamera.cpp +++ b/src/datavisualization/engine/q3dcamera.cpp @@ -421,6 +421,9 @@ void Q3DCamera::setCameraPreset(CameraPreset preset) break; } + // All presets target the center of the graph + setTarget(zeroVector); + if (d_ptr->m_activePreset != preset) { d_ptr->m_activePreset = preset; setDirty(true); @@ -604,6 +607,8 @@ void Q3DCamera::setTarget(const QVector3D &target) newTarget.setZ(1.0f); if (d_ptr->m_requestedTarget != newTarget) { + if (d_ptr->m_activePreset != CameraPresetNone) + d_ptr->m_activePreset = CameraPresetNone; d_ptr->m_requestedTarget = newTarget; setDirty(true); emit targetChanged(newTarget); -- cgit v1.2.3 From 4f70d3777e036f131cf8d39e1072b276132e32a1 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Mon, 13 Oct 2014 15:55:32 +0300 Subject: Fix gradient artifacts on some edge cases MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When static optimization is in use with mesh objects and range gradient, and the object Y-value resolves into a texture coordinate that is exactly on the texel boundary, the rendered fragments of the object are not all same colors on some graphics cards, despite all vertices having the same UV value. Fixed by adjusting the Y-value slightly if it is close to the boundary. Task-number: QTRD-3370 Change-Id: Ie028602cbd9a00bb0e17049eb8f40feb8b18a6bf Reviewed-by: Tomi Korpipää --- src/datavisualization/utils/scatterobjectbufferhelper.cpp | 13 +++++++++++++ tests/scattertest/scatterchart.cpp | 15 ++++++++++++++- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/src/datavisualization/utils/scatterobjectbufferhelper.cpp b/src/datavisualization/utils/scatterobjectbufferhelper.cpp index dae60b96..6135c225 100644 --- a/src/datavisualization/utils/scatterobjectbufferhelper.cpp +++ b/src/datavisualization/utils/scatterobjectbufferhelper.cpp @@ -20,6 +20,7 @@ #include "objecthelper_p.h" #include #include +#include QT_BEGIN_NAMESPACE_DATAVISUALIZATION @@ -232,6 +233,8 @@ uint ScatterObjectBufferHelper::createRangeGradientUVs(ScatterSeriesRenderCache const ScatterRenderItemArray &renderArray = cache->renderArray(); const bool updateAll = (cache->updateIndices().size() == 0); const int updateSize = updateAll ? renderArray.size() : cache->updateIndices().size(); + const float yAdjustment = 0.1f / gradientTextureHeight; + const float flippedYAdjustment = 0.9f / gradientTextureHeight; QVector2D uv; uv.setX(0.0f); @@ -243,7 +246,17 @@ uint ScatterObjectBufferHelper::createRangeGradientUVs(ScatterSeriesRenderCache continue; float y = ((item.translation().y() + m_scaleY) * 0.5f) / m_scaleY; + + // Avoid values near gradient texel boundary, as this causes artifacts + // with some graphics cards. + const float floorY = float(qFloor(y * gradientTextureHeight)); + const float diff = y - floorY; + if (diff < yAdjustment) + y += yAdjustment; + else if (diff > flippedYAdjustment) + y -= yAdjustment; uv.setY(y); + int offset = pos * uvsCount; for (int j = 0; j < uvsCount; j++) buffered_uvs[j + offset] = uv; diff --git a/tests/scattertest/scatterchart.cpp b/tests/scattertest/scatterchart.cpp index c7b93c32..e5a7acc3 100644 --- a/tests/scattertest/scatterchart.cpp +++ b/tests/scattertest/scatterchart.cpp @@ -723,8 +723,21 @@ void ScatterDataModifier::changeBunch() if (m_targetSeries->dataProxy()->array()->size()) { int amount = qMin(m_targetSeries->dataProxy()->array()->size(), 100); QScatterDataArray items(amount); - for (int i = 0; i < items.size(); i++) + for (int i = 0; i < items.size(); i++) { items[i].setPosition(randVector()); + // Change the Y-values of first few items to exact gradient boundaries + if (i == 0) + items[i].setY(0.65f); + else if (i == 1) + items[i].setY(0.1f); + else if (i == 2) + items[i].setY(-0.45f); + else if (i == 3) + items[i].setY(-1.0f); + else if (i == 4) + items[i].setY(1.2f); + } + m_targetSeries->dataProxy()->setItems(0, items); qDebug() << m_loopCounter << "Changed bunch, array size:" << m_targetSeries->dataProxy()->array()->size(); } -- cgit v1.2.3 From 961600f6549756754ae155ef9cf63bb990e00f2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomi=20Korpip=C3=A4=C3=A4?= Date: Tue, 14 Oct 2014 07:32:49 +0300 Subject: Bars3D, Surface3D and Scatter3D tests Task-number: QTRD-3368 + Removed erroneous property from docs Change-Id: I675e16b58e7b190b2b33d29029654039a60b5bfc Change-Id: I675e16b58e7b190b2b33d29029654039a60b5bfc Reviewed-by: Miikka Heikkinen --- .../doc/src/qtdatavisualization-qml-surface3d.qdoc | 5 - tests/auto/qmltest/bars3d/tst_bars.qml | 147 +++++++++++++++++++++ tests/auto/qmltest/customitem.obj | 54 ++++++++ tests/auto/qmltest/qmltest.pro | 8 +- tests/auto/qmltest/qmltest.qrc | 5 + tests/auto/qmltest/scatter3d/tst_scatter.qml | 60 +++++++++ tests/auto/qmltest/surface3d/tst_surface.qml | 60 +++++++++ 7 files changed, 333 insertions(+), 6 deletions(-) create mode 100644 tests/auto/qmltest/bars3d/tst_bars.qml create mode 100644 tests/auto/qmltest/customitem.obj create mode 100644 tests/auto/qmltest/qmltest.qrc create mode 100644 tests/auto/qmltest/scatter3d/tst_scatter.qml create mode 100644 tests/auto/qmltest/surface3d/tst_surface.qml diff --git a/src/datavisualization/doc/src/qtdatavisualization-qml-surface3d.qdoc b/src/datavisualization/doc/src/qtdatavisualization-qml-surface3d.qdoc index 2e343d38..2b83b807 100644 --- a/src/datavisualization/doc/src/qtdatavisualization-qml-surface3d.qdoc +++ b/src/datavisualization/doc/src/qtdatavisualization-qml-surface3d.qdoc @@ -72,11 +72,6 @@ * \c SelectionMultiSeries flag set, this property holds the series which owns the selected point. */ -/*! - \qmlproperty ColorGradient Surface3D::gradient - The current surface gradient. Setting this property replaces the previous gradient. - */ - /*! * \qmlproperty list Surface3D::seriesList * \default diff --git a/tests/auto/qmltest/bars3d/tst_bars.qml b/tests/auto/qmltest/bars3d/tst_bars.qml new file mode 100644 index 00000000..a64aaf1a --- /dev/null +++ b/tests/auto/qmltest/bars3d/tst_bars.qml @@ -0,0 +1,147 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +import QtQuick 2.0 +import QtDataVisualization 1.2 +import QtTest 1.0 + +Item { + id: top + height: 150 + width: 150 + + Bars3D { + id: series + anchors.fill: parent + } + + TestCase { + name: "Bars3D Series" + + Bar3DSeries { id: series1 } + Bar3DSeries { id: series2 } + + function test_1_add_series() { + series.seriesList = [series1, series2] + compare(series.seriesList.length, 2) + } + + function test_2_remove_series() { + series.seriesList = [series1] + compare(series.seriesList.length, 1) + } + + function test_3_remove_series() { + series.seriesList = [] + compare(series.seriesList.length, 0) + } + + function test_4_primary_series() { + series.seriesList = [series1, series2] + compare(series.primarySeries, series1) + series.primarySeries = series2 + compare(series.primarySeries, series2) + } + + function test_5_selected_series() { + series.seriesList[0].selectedBar = Qt.point(0, 0) + compare(series.selectedSeries, series1) + } + } + + // The following tests are not required for scatter or surface, as they are handled identically + Bars3D { + id: theme + anchors.fill: parent + } + + Bars3D { + id: input + anchors.fill: parent + } + + Custom3DItem { id: item1; meshFile: ":/customitem.obj" } + Custom3DItem { id: item2; meshFile: ":/customitem.obj" } + Custom3DItem { id: item3; meshFile: ":/customitem.obj" } + Custom3DItem { id: item4; meshFile: ":/customitem.obj"; position: Qt.vector3d(0.0, 1.0, 0.0) } + + Bars3D { + id: custom + anchors.fill: parent + customItemList: [item1, item2] + } + + TestCase { + name: "Bars3D Theme" + when: windowShown + + Theme3D { id: newTheme } + + function test_1_add_theme() { + theme.theme = newTheme + compare(theme.theme, newTheme) + } + + function test_2_change_theme() { + newTheme.type = Theme3D.ThemePrimaryColors + compare(theme.theme.type, Theme3D.ThemePrimaryColors) + } + } + + TestCase { + name: "Bars3D Input" + when: windowShown + + function test_1_remove_input() { + input.inputHandler = null + compare(input.inputHandler, null) + } + } + + TestCase { + name: "Bars3D Custom" + when: windowShown + + function test_1_custom_items() { + compare(custom.customItemList.length, 2) + } + + function test_2_add_custom_items() { + custom.addCustomItem(item3) + compare(custom.customItemList.length, 3) + custom.addCustomItem(item4) + compare(custom.customItemList.length, 4) + } + + function test_3_change_custom_items() { + item1.position = Qt.vector3d(1.0, 1.0, 1.0) + compare(custom.customItemList[0].position, Qt.vector3d(1.0, 1.0, 1.0)) + } + + function test_4_remove_custom_items() { + custom.removeCustomItemAt(Qt.vector3d(0.0, 1.0, 0.0)) + compare(custom.customItemList.length, 3) + custom.releaseCustomItem(item1) + compare(custom.customItemList[0], item2) + custom.releaseCustomItem(item2) + compare(custom.customItemList.length, 1) + custom.removeCustomItems() + compare(custom.customItemList.length, 0) + } + } +} diff --git a/tests/auto/qmltest/customitem.obj b/tests/auto/qmltest/customitem.obj new file mode 100644 index 00000000..108cf7ac --- /dev/null +++ b/tests/auto/qmltest/customitem.obj @@ -0,0 +1,54 @@ +# Blender v2.66 (sub 0) OBJ File: 'cube_filled.blend' +# www.blender.org +o Cube +v -1.000000 -1.000000 1.000000 +v -1.000000 -1.000000 -1.000000 +v 1.000000 -1.000000 -1.000000 +v 1.000000 -1.000000 1.000000 +v -1.000000 1.000000 1.000000 +v -1.000000 1.000000 -1.000000 +v 1.000000 1.000000 -1.000000 +v 1.000000 1.000000 1.000000 +vt 0.666667 0.332314 +vt 0.334353 0.333333 +vt 0.665647 0.000000 +vt 0.001020 0.333333 +vt 0.000000 0.001020 +vt 0.333333 0.332314 +vt 0.333333 0.665647 +vt 0.001019 0.666667 +vt 0.000000 0.334353 +vt 0.334353 0.666667 +vt 0.333333 0.334353 +vt 0.665647 0.333333 +vt 0.333333 0.667686 +vt 0.665647 0.666667 +vt 0.666667 0.998980 +vt 0.667686 0.333333 +vt 0.666667 0.001019 +vt 0.998980 0.000000 +vt 0.333333 0.001019 +vt 0.332314 0.000000 +vt 0.332314 0.333333 +vt 0.666667 0.665647 +vt 0.334353 1.000000 +vt 1.000000 0.332314 +vn -1.000000 0.000000 0.000000 +vn 0.000000 0.000000 -1.000000 +vn 1.000000 -0.000000 0.000000 +vn 0.000000 0.000000 1.000000 +vn 0.000000 1.000000 0.000000 +vn -0.000000 -1.000000 -0.000000 +s off +f 5/1/1 6/2/1 1/3/1 +f 6/4/2 7/5/2 2/6/2 +f 7/7/3 8/8/3 4/9/3 +f 8/10/4 5/11/4 1/12/4 +f 8/13/5 7/14/5 6/15/5 +f 2/16/6 3/17/6 4/18/6 +f 6/2/1 2/19/1 1/3/1 +f 7/5/2 3/20/2 2/6/2 +f 3/21/3 7/7/3 4/9/3 +f 4/22/4 8/10/4 1/12/4 +f 5/23/5 8/13/5 6/15/5 +f 1/24/6 2/16/6 4/18/6 diff --git a/tests/auto/qmltest/qmltest.pro b/tests/auto/qmltest/qmltest.pro index b2f977eb..e1901817 100644 --- a/tests/auto/qmltest/qmltest.pro +++ b/tests/auto/qmltest/qmltest.pro @@ -4,5 +4,11 @@ CONFIG += qmltestcase CONFIG += console SOURCES += tst_qmltest.cpp OTHER_FILES += bars3d\tst_basic.qml \ + bars3d\tst_bars.qml \ scatter3d\tst_basic.qml \ - surface3d\tst_basic.qml + scatter3d\tst_scatter.qml \ + surface3d\tst_basic.qml \ + surface3d\tst_surface.qml + +RESOURCES += \ + qmltest.qrc diff --git a/tests/auto/qmltest/qmltest.qrc b/tests/auto/qmltest/qmltest.qrc new file mode 100644 index 00000000..69def7f8 --- /dev/null +++ b/tests/auto/qmltest/qmltest.qrc @@ -0,0 +1,5 @@ + + + customitem.obj + + diff --git a/tests/auto/qmltest/scatter3d/tst_scatter.qml b/tests/auto/qmltest/scatter3d/tst_scatter.qml new file mode 100644 index 00000000..b070b5f0 --- /dev/null +++ b/tests/auto/qmltest/scatter3d/tst_scatter.qml @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +import QtQuick 2.0 +import QtDataVisualization 1.2 +import QtTest 1.0 + +Item { + id: top + height: 150 + width: 150 + + Scatter3D { + id: series + anchors.fill: parent + } + + TestCase { + name: "Scatter3D Series" + + Scatter3DSeries { id: series1 } + Scatter3DSeries { id: series2 } + + function test_1_add_series() { + series.seriesList = [series1, series2] + compare(series.seriesList.length, 2) + } + + function test_2_remove_series() { + series.seriesList = [series1] + compare(series.seriesList.length, 1) + } + + function test_3_remove_series() { + series.seriesList = [] + compare(series.seriesList.length, 0) + } + + function test_4_selected_series() { + series.seriesList = [series1, series2] + series.seriesList[0].selectedItem = 0 + compare(series.selectedSeries, series1) + } + } +} diff --git a/tests/auto/qmltest/surface3d/tst_surface.qml b/tests/auto/qmltest/surface3d/tst_surface.qml new file mode 100644 index 00000000..31c86da2 --- /dev/null +++ b/tests/auto/qmltest/surface3d/tst_surface.qml @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +import QtQuick 2.0 +import QtDataVisualization 1.2 +import QtTest 1.0 + +Item { + id: top + height: 150 + width: 150 + + Surface3D { + id: series + anchors.fill: parent + } + + TestCase { + name: "Surface3D Series" + + Surface3DSeries { id: series1 } + Surface3DSeries { id: series2 } + + function test_1_add_series() { + series.seriesList = [series1, series2] + compare(series.seriesList.length, 2) + } + + function test_2_remove_series() { + series.seriesList = [series1] + compare(series.seriesList.length, 1) + } + + function test_3_remove_series() { + series.seriesList = [] + compare(series.seriesList.length, 0) + } + + function test_4_selected_series() { + series.seriesList = [series1, series2] + series.seriesList[0].selectedPoint = Qt.point(0, 0) + compare(series.selectedSeries, series1) + } + } +} -- cgit v1.2.3 From 7d954d485255acd7f40d6facb1b90e75e375641f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomi=20Korpip=C3=A4=C3=A4?= Date: Tue, 14 Oct 2014 11:32:04 +0300 Subject: QML series tests added Task-number: QTRD-3368 Change-Id: I80ecdb1a9bcef6ed83d278d975d70f7794ff03c1 Reviewed-by: Miikka Heikkinen --- tests/auto/qmltest/bars3d/tst_barseries.qml | 215 ++++++++++++++++++++ tests/auto/qmltest/qmltest.pro | 5 +- tests/auto/qmltest/scatter3d/tst_scatterseries.qml | 219 ++++++++++++++++++++ tests/auto/qmltest/surface3d/tst_surfaceseries.qml | 225 +++++++++++++++++++++ 4 files changed, 663 insertions(+), 1 deletion(-) create mode 100644 tests/auto/qmltest/bars3d/tst_barseries.qml create mode 100644 tests/auto/qmltest/scatter3d/tst_scatterseries.qml create mode 100644 tests/auto/qmltest/surface3d/tst_surfaceseries.qml diff --git a/tests/auto/qmltest/bars3d/tst_barseries.qml b/tests/auto/qmltest/bars3d/tst_barseries.qml new file mode 100644 index 00000000..7e303ab0 --- /dev/null +++ b/tests/auto/qmltest/bars3d/tst_barseries.qml @@ -0,0 +1,215 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +import QtQuick 2.0 +import QtDataVisualization 1.2 +import QtTest 1.0 + +Item { + id: top + height: 150 + width: 150 + + Bar3DSeries { + id: initial + } + + ColorGradient { + id: gradient1; + stops: [ + ColorGradientStop { color: "red"; position: 0 }, + ColorGradientStop { color: "blue"; position: 1 } + ] + } + + ColorGradient { + id: gradient2; + stops: [ + ColorGradientStop { color: "green"; position: 0 }, + ColorGradientStop { color: "red"; position: 1 } + ] + } + + ColorGradient { + id: gradient3; + stops: [ + ColorGradientStop { color: "gray"; position: 0 }, + ColorGradientStop { color: "darkgray"; position: 1 } + ] + } + + Bar3DSeries { + id: initialized + dataProxy: ItemModelBarDataProxy { + itemModel: ListModel { + ListElement{ year: "2012"; city: "Oulu"; expenses: "4200"; } + ListElement{ year: "2012"; city: "Rauma"; expenses: "2100"; } + } + rowRole: "city" + columnRole: "year" + valueRole: "expenses" + } + meshAngle: 15.0 + selectedBar: Qt.point(0, 0) + + baseColor: "blue" + baseGradient: gradient1 + colorStyle: Theme3D.ColorStyleObjectGradient + itemLabelFormat: "%f" + itemLabelVisible: false + mesh: Abstract3DSeries.MeshCone + meshSmooth: true + multiHighlightColor: "green" + multiHighlightGradient: gradient2 + name: "series1" + singleHighlightColor: "red" + singleHighlightGradient: gradient3 + userDefinedMesh: ":/customitem.obj" + visible: false + } + + ItemModelBarDataProxy { + id: proxy1 + itemModel: ListModel { + ListElement{ year: "2012"; city: "Oulu"; expenses: "4200"; } + ListElement{ year: "2012"; city: "Rauma"; expenses: "2100"; } + ListElement{ year: "2012"; city: "Helsinki"; expenses: "7040"; } + } + rowRole: "city" + columnRole: "year" + valueRole: "expenses" + } + + Bar3DSeries { + id: change + } + + TestCase { + name: "Bar3DSeries Initial" + + function test_1_initial() { + compare(initial.dataProxy.rowCount, 0) + compare(initial.invalidSelectionPosition, Qt.point(-1, -1)) + compare(initial.meshAngle, 0) + compare(initial.selectedBar, Qt.point(-1, -1)) + } + + function test_2_initial_common() { + // Common properties + compare(initial.baseColor, "#000000") + compare(initial.baseGradient, null) + compare(initial.colorStyle, Theme3D.ColorStyleUniform) + compare(initial.itemLabel, "") + compare(initial.itemLabelFormat, "@valueLabel") + compare(initial.itemLabelVisible, true) + compare(initial.mesh, Abstract3DSeries.MeshBevelBar) + compare(initial.meshRotation, Qt.quaternion(1, 0, 0, 0)) + compare(initial.meshSmooth, false) + compare(initial.multiHighlightColor, "#000000") + compare(initial.multiHighlightGradient, null) + compare(initial.name, "") + compare(initial.singleHighlightColor, "#000000") + compare(initial.singleHighlightGradient, null) + compare(initial.type, Abstract3DSeries.SeriesTypeBar) + compare(initial.userDefinedMesh, "") + compare(initial.visible, true) + } + } + + TestCase { + name: "Bar3DSeries Initialized" + + function test_1_initialized() { + compare(initialized.dataProxy.rowCount, 2) + fuzzyCompare(initialized.meshAngle, 15.0, 0.01) + compare(initialized.selectedBar, Qt.point(0, 0)) + } + + function test_2_initialized_common() { + // Common properties + compare(initialized.baseColor, "#0000ff") + compare(initialized.baseGradient, gradient1) + compare(initialized.colorStyle, Theme3D.ColorStyleObjectGradient) + compare(initialized.itemLabelFormat, "%f") + compare(initialized.itemLabelVisible, false) + compare(initialized.mesh, Abstract3DSeries.MeshCone) + compare(initialized.meshSmooth, true) + compare(initialized.multiHighlightColor, "#008000") + compare(initialized.multiHighlightGradient, gradient2) + compare(initialized.name, "series1") + compare(initialized.singleHighlightColor, "#ff0000") + compare(initialized.singleHighlightGradient, gradient3) + compare(initialized.userDefinedMesh, ":/customitem.obj") + compare(initialized.visible, false) + } + } + + TestCase { + name: "Bar3DSeries Change" + + function test_1_change() { + change.dataProxy = proxy1 + change.meshAngle = 15.0 + change.selectedBar = Qt.point(0, 0) + } + + function test_2_test_change() { + // This test has a dependency to the previous one due to asynchronous item model resolving + compare(change.dataProxy.rowCount, 3) + fuzzyCompare(change.meshAngle, 15.0, 0.01) + compare(change.selectedBar, Qt.point(0, 0)) + } + + function test_3_change_common() { + change.baseColor = "blue" + change.baseGradient = gradient1 + change.colorStyle = Theme3D.ColorStyleObjectGradient + change.itemLabelFormat = "%f" + change.itemLabelVisible = false + change.mesh = Abstract3DSeries.MeshCone + change.meshSmooth = true + change.multiHighlightColor = "green" + change.multiHighlightGradient = gradient2 + change.name = "series1" + change.singleHighlightColor = "red" + change.singleHighlightGradient = gradient3 + change.userDefinedMesh = ":/customitem.obj" + change.visible = false + + compare(change.baseColor, "#0000ff") + compare(change.baseGradient, gradient1) + compare(change.colorStyle, Theme3D.ColorStyleObjectGradient) + compare(change.itemLabelFormat, "%f") + compare(change.itemLabelVisible, false) + compare(change.mesh, Abstract3DSeries.MeshCone) + compare(change.meshSmooth, true) + compare(change.multiHighlightColor, "#008000") + compare(change.multiHighlightGradient, gradient2) + compare(change.name, "series1") + compare(change.singleHighlightColor, "#ff0000") + compare(change.singleHighlightGradient, gradient3) + compare(change.userDefinedMesh, ":/customitem.obj") + compare(change.visible, false) + } + + function test_4_change_gradient_stop() { + gradient1.stops[0].color = "yellow" + compare(change.baseGradient.stops[0].color, "#ffff00") + } + } +} diff --git a/tests/auto/qmltest/qmltest.pro b/tests/auto/qmltest/qmltest.pro index e1901817..a6430862 100644 --- a/tests/auto/qmltest/qmltest.pro +++ b/tests/auto/qmltest/qmltest.pro @@ -5,10 +5,13 @@ CONFIG += console SOURCES += tst_qmltest.cpp OTHER_FILES += bars3d\tst_basic.qml \ bars3d\tst_bars.qml \ + bars3d\tst_barseries.qml \ scatter3d\tst_basic.qml \ scatter3d\tst_scatter.qml \ + scatter3d\tst_scatterseries.qml \ surface3d\tst_basic.qml \ - surface3d\tst_surface.qml + surface3d\tst_surface.qml \ + surface3d\tst_surfaceseries.qml RESOURCES += \ qmltest.qrc diff --git a/tests/auto/qmltest/scatter3d/tst_scatterseries.qml b/tests/auto/qmltest/scatter3d/tst_scatterseries.qml new file mode 100644 index 00000000..e42443ad --- /dev/null +++ b/tests/auto/qmltest/scatter3d/tst_scatterseries.qml @@ -0,0 +1,219 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +import QtQuick 2.0 +import QtDataVisualization 1.2 +import QtTest 1.0 + +Item { + id: top + height: 150 + width: 150 + + Scatter3DSeries { + id: initial + } + + ColorGradient { + id: gradient1; + stops: [ + ColorGradientStop { color: "red"; position: 0 }, + ColorGradientStop { color: "blue"; position: 1 } + ] + } + + ColorGradient { + id: gradient2; + stops: [ + ColorGradientStop { color: "green"; position: 0 }, + ColorGradientStop { color: "red"; position: 1 } + ] + } + + ColorGradient { + id: gradient3; + stops: [ + ColorGradientStop { color: "gray"; position: 0 }, + ColorGradientStop { color: "darkgray"; position: 1 } + ] + } + + Scatter3DSeries { + id: initialized + dataProxy: ItemModelScatterDataProxy { + itemModel: ListModel { + ListElement{ xPos: "2.754"; yPos: "1.455"; zPos: "3.362"; } + ListElement{ xPos: "3.164"; yPos: "2.022"; zPos: "4.348"; } + } + xPosRole: "xPos" + yPosRole: "yPos" + zPosRole: "zPos" + } + itemSize: 0.5 + selectedItem: 0 + + baseColor: "blue" + baseGradient: gradient1 + colorStyle: Theme3D.ColorStyleObjectGradient + itemLabelFormat: "%f" + itemLabelVisible: false + mesh: Abstract3DSeries.MeshMinimal + meshRotation: Qt.quaternion(1, 1, 1, 1) + meshSmooth: true + multiHighlightColor: "green" + multiHighlightGradient: gradient2 + name: "series1" + singleHighlightColor: "red" + singleHighlightGradient: gradient3 + userDefinedMesh: ":/customitem.obj" + visible: false + } + + ItemModelScatterDataProxy { + id: proxy1 + itemModel: ListModel { + ListElement{ xPos: "2.754"; yPos: "1.455"; zPos: "3.362"; } + ListElement{ xPos: "3.164"; yPos: "2.022"; zPos: "4.348"; } + ListElement{ xPos: "4.564"; yPos: "1.865"; zPos: "1.346"; } + } + xPosRole: "xPos" + yPosRole: "yPos" + zPosRole: "zPos" + } + + Scatter3DSeries { + id: change + } + + TestCase { + name: "Scatter3DSeries Initial" + + function test_1_initial() { + compare(initial.dataProxy.itemCount, 0) + compare(initial.invalidSelectionIndex, -1) + compare(initial.itemSize, 0.0) + compare(initial.selectedItem, -1) + } + + function test_2_initial_common() { + // Common properties + compare(initial.baseColor, "#000000") + compare(initial.baseGradient, null) + compare(initial.colorStyle, Theme3D.ColorStyleUniform) + compare(initial.itemLabel, "") + compare(initial.itemLabelFormat, "@xLabel, @yLabel, @zLabel") + compare(initial.itemLabelVisible, true) + compare(initial.mesh, Abstract3DSeries.MeshSphere) + compare(initial.meshRotation, Qt.quaternion(1, 0, 0, 0)) + compare(initial.meshSmooth, false) + compare(initial.multiHighlightColor, "#000000") + compare(initial.multiHighlightGradient, null) + compare(initial.name, "") + compare(initial.singleHighlightColor, "#000000") + compare(initial.singleHighlightGradient, null) + compare(initial.type, Abstract3DSeries.SeriesTypeScatter) + compare(initial.userDefinedMesh, "") + compare(initial.visible, true) + } + } + + TestCase { + name: "Scatter3DSeries Initialized" + + function test_1_initialized() { + compare(initialized.dataProxy.itemCount, 2) + compare(initialized.itemSize, 0.5) + compare(initialized.selectedItem, 0) + } + + function test_2_initialized_common() { + // Common properties + compare(initialized.baseColor, "#0000ff") + compare(initialized.baseGradient, gradient1) + compare(initialized.colorStyle, Theme3D.ColorStyleObjectGradient) + compare(initialized.itemLabelFormat, "%f") + compare(initialized.itemLabelVisible, false) + compare(initialized.mesh, Abstract3DSeries.MeshMinimal) + compare(initialized.meshRotation, Qt.quaternion(1, 1, 1, 1)) + compare(initialized.meshSmooth, true) + compare(initialized.multiHighlightColor, "#008000") + compare(initialized.multiHighlightGradient, gradient2) + compare(initialized.name, "series1") + compare(initialized.singleHighlightColor, "#ff0000") + compare(initialized.singleHighlightGradient, gradient3) + compare(initialized.userDefinedMesh, ":/customitem.obj") + compare(initialized.visible, false) + } + } + + TestCase { + name: "Scatter3DSeries Change" + + function test_1_change() { + change.dataProxy = proxy1 + change.itemSize = 0.5 + change.selectedItem = 0 + } + + function test_2_test_change() { + // This test has a dependency to the previous one due to asynchronous item model resolving + compare(change.dataProxy.itemCount, 3) + compare(change.itemSize, 0.5) + compare(change.selectedItem, 0) + } + + function test_3_change_common() { + change.baseColor = "blue" + change.baseGradient = gradient1 + change.colorStyle = Theme3D.ColorStyleObjectGradient + change.itemLabelFormat = "%f" + change.itemLabelVisible = false + change.mesh = Abstract3DSeries.MeshMinimal + change.meshRotation = Qt.quaternion(1, 1, 1, 1) + change.meshSmooth = true + change.multiHighlightColor = "green" + change.multiHighlightGradient = gradient2 + change.name = "series1" + change.singleHighlightColor = "red" + change.singleHighlightGradient = gradient3 + change.userDefinedMesh = ":/customitem.obj" + change.visible = false + + compare(change.baseColor, "#0000ff") + compare(change.baseGradient, gradient1) + compare(change.colorStyle, Theme3D.ColorStyleObjectGradient) + compare(change.itemLabelFormat, "%f") + compare(change.itemLabelVisible, false) + compare(change.mesh, Abstract3DSeries.MeshMinimal) + compare(change.meshRotation, Qt.quaternion(1, 1, 1, 1)) + compare(change.meshSmooth, true) + compare(change.multiHighlightColor, "#008000") + compare(change.multiHighlightGradient, gradient2) + compare(change.name, "series1") + compare(change.singleHighlightColor, "#ff0000") + compare(change.singleHighlightGradient, gradient3) + compare(change.userDefinedMesh, ":/customitem.obj") + compare(change.visible, false) + } + + function test_4_change_gradient_stop() { + gradient1.stops[0].color = "yellow" + compare(change.baseGradient.stops[0].color, "#ffff00") + } + } +} diff --git a/tests/auto/qmltest/surface3d/tst_surfaceseries.qml b/tests/auto/qmltest/surface3d/tst_surfaceseries.qml new file mode 100644 index 00000000..866a04b9 --- /dev/null +++ b/tests/auto/qmltest/surface3d/tst_surfaceseries.qml @@ -0,0 +1,225 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +import QtQuick 2.0 +import QtDataVisualization 1.2 +import QtTest 1.0 + +Item { + id: top + height: 150 + width: 150 + + Surface3DSeries { + id: initial + } + + ColorGradient { + id: gradient1; + stops: [ + ColorGradientStop { color: "red"; position: 0 }, + ColorGradientStop { color: "blue"; position: 1 } + ] + } + + ColorGradient { + id: gradient2; + stops: [ + ColorGradientStop { color: "green"; position: 0 }, + ColorGradientStop { color: "red"; position: 1 } + ] + } + + ColorGradient { + id: gradient3; + stops: [ + ColorGradientStop { color: "gray"; position: 0 }, + ColorGradientStop { color: "darkgray"; position: 1 } + ] + } + + Surface3DSeries { + id: initialized + dataProxy: ItemModelSurfaceDataProxy { + itemModel: ListModel { + ListElement{ longitude: "20"; latitude: "10"; pop_density: "4.75"; } + ListElement{ longitude: "21"; latitude: "10"; pop_density: "3.00"; } + } + rowRole: "longitude" + columnRole: "latitude" + yPosRole: "pop_density" + } + drawMode: Surface3DSeries.DrawSurface + flatShadingEnabled: false + selectedPoint: Qt.point(0, 0) + + baseColor: "blue" + baseGradient: gradient1 + colorStyle: Theme3D.ColorStyleObjectGradient + itemLabelFormat: "%f" + itemLabelVisible: false + mesh: Abstract3DSeries.MeshCube + meshRotation: Qt.quaternion(1, 1, 1, 1) + meshSmooth: true + multiHighlightColor: "green" + multiHighlightGradient: gradient2 + name: "series1" + singleHighlightColor: "red" + singleHighlightGradient: gradient3 + userDefinedMesh: ":/customitem.obj" + visible: false + } + + ItemModelSurfaceDataProxy { + id: proxy1 + itemModel: ListModel { + ListElement{ longitude: "20"; latitude: "10"; pop_density: "4.75"; } + ListElement{ longitude: "21"; latitude: "10"; pop_density: "3.00"; } + ListElement{ longitude: "22"; latitude: "10"; pop_density: "1.24"; } + } + rowRole: "longitude" + columnRole: "latitude" + yPosRole: "pop_density" + } + + Surface3DSeries { + id: change + } + + TestCase { + name: "Surface3DSeries Initial" + + function test_1_initial() { + compare(initial.dataProxy.rowCount, 0) + compare(initial.invalidSelectionPosition, Qt.point(-1, -1)) + compare(initial.drawMode, Surface3DSeries.DrawSurfaceAndWireframe) + compare(initial.flatShadingEnabled, true) + compare(initial.flatShadingSupported, true) + compare(initial.selectedPoint, Qt.point(-1, -1)) + } + + function test_2_initial_common() { + // Common properties + compare(initial.baseColor, "#000000") + compare(initial.baseGradient, null) + compare(initial.colorStyle, Theme3D.ColorStyleUniform) + compare(initial.itemLabel, "") + compare(initial.itemLabelFormat, "@xLabel, @yLabel, @zLabel") + compare(initial.itemLabelVisible, true) + compare(initial.mesh, Abstract3DSeries.MeshSphere) + compare(initial.meshRotation, Qt.quaternion(1, 0, 0, 0)) + compare(initial.meshSmooth, false) + compare(initial.multiHighlightColor, "#000000") + compare(initial.multiHighlightGradient, null) + compare(initial.name, "") + compare(initial.singleHighlightColor, "#000000") + compare(initial.singleHighlightGradient, null) + compare(initial.type, Abstract3DSeries.SeriesTypeSurface) + compare(initial.userDefinedMesh, "") + compare(initial.visible, true) + } + } + + TestCase { + name: "Surface3DSeries Initialized" + + function test_1_initialized() { + compare(initialized.dataProxy.rowCount, 2) + compare(initialized.drawMode, Surface3DSeries.DrawSurface) + compare(initialized.flatShadingEnabled, false) + compare(initialized.selectedPoint, Qt.point(0, 0)) + } + + function test_2_initialized_common() { + // Common properties + compare(initialized.baseColor, "#0000ff") + compare(initialized.baseGradient, gradient1) + compare(initialized.colorStyle, Theme3D.ColorStyleObjectGradient) + compare(initialized.itemLabelFormat, "%f") + compare(initialized.itemLabelVisible, false) + compare(initialized.mesh, Abstract3DSeries.MeshCube) + compare(initialized.meshRotation, Qt.quaternion(1, 1, 1, 1)) + compare(initialized.meshSmooth, true) + compare(initialized.multiHighlightColor, "#008000") + compare(initialized.multiHighlightGradient, gradient2) + compare(initialized.name, "series1") + compare(initialized.singleHighlightColor, "#ff0000") + compare(initialized.singleHighlightGradient, gradient3) + compare(initialized.userDefinedMesh, ":/customitem.obj") + compare(initialized.visible, false) + } + } + + TestCase { + name: "Surface3DSeries Change" + + function test_1_change() { + change.dataProxy = proxy1 + change.drawMode = Surface3DSeries.DrawSurface + change.flatShadingEnabled = false + change.selectedPoint = Qt.point(0, 0) + } + + function test_2_test_change() { + // This test has a dependency to the previous one due to asynchronous item model resolving + compare(change.dataProxy.rowCount, 3) + compare(change.drawMode, Surface3DSeries.DrawSurface) + compare(change.flatShadingEnabled, false) + compare(change.selectedPoint, Qt.point(0, 0)) + } + + function test_3_change_common() { + change.baseColor = "blue" + change.baseGradient = gradient1 + change.colorStyle = Theme3D.ColorStyleObjectGradient + change.itemLabelFormat = "%f" + change.itemLabelVisible = false + change.mesh = Abstract3DSeries.MeshCube + change.meshRotation = Qt.quaternion(1, 1, 1, 1) + change.meshSmooth = true + change.multiHighlightColor = "green" + change.multiHighlightGradient = gradient2 + change.name = "series1" + change.singleHighlightColor = "red" + change.singleHighlightGradient = gradient3 + change.userDefinedMesh = ":/customitem.obj" + change.visible = false + + compare(change.baseColor, "#0000ff") + compare(change.baseGradient, gradient1) + compare(change.colorStyle, Theme3D.ColorStyleObjectGradient) + compare(change.itemLabelFormat, "%f") + compare(change.itemLabelVisible, false) + compare(change.mesh, Abstract3DSeries.MeshCube) + compare(change.meshRotation, Qt.quaternion(1, 1, 1, 1)) + compare(change.meshSmooth, true) + compare(change.multiHighlightColor, "#008000") + compare(change.multiHighlightGradient, gradient2) + compare(change.name, "series1") + compare(change.singleHighlightColor, "#ff0000") + compare(change.singleHighlightGradient, gradient3) + compare(change.userDefinedMesh, ":/customitem.obj") + compare(change.visible, false) + } + + function test_4_change_gradient_stop() { + gradient1.stops[0].color = "yellow" + compare(change.baseGradient.stops[0].color, "#ffff00") + } + } +} -- cgit v1.2.3 From d0244a84f150fe59327afa9bd59d0f9cdde72406 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Tue, 14 Oct 2014 13:29:18 +0300 Subject: Gradient adjustment logic was broken. Bug in adjustment logic caused adjustment to happen pretty much always. Task-number: QTRD-3374 Change-Id: Id6151d2d3eb8369bbf9252e4eb4b76ed57fb1f3c Reviewed-by: Mika Salmela --- src/datavisualization/utils/scatterobjectbufferhelper.cpp | 10 +++++----- tests/scattertest/scatterchart.cpp | 4 ++++ 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/datavisualization/utils/scatterobjectbufferhelper.cpp b/src/datavisualization/utils/scatterobjectbufferhelper.cpp index 6135c225..8925a7a4 100644 --- a/src/datavisualization/utils/scatterobjectbufferhelper.cpp +++ b/src/datavisualization/utils/scatterobjectbufferhelper.cpp @@ -233,8 +233,8 @@ uint ScatterObjectBufferHelper::createRangeGradientUVs(ScatterSeriesRenderCache const ScatterRenderItemArray &renderArray = cache->renderArray(); const bool updateAll = (cache->updateIndices().size() == 0); const int updateSize = updateAll ? renderArray.size() : cache->updateIndices().size(); - const float yAdjustment = 0.1f / gradientTextureHeight; - const float flippedYAdjustment = 0.9f / gradientTextureHeight; + const float yAdjustment = 0.1f; + const float flippedYAdjustment = 0.9f; QVector2D uv; uv.setX(0.0f); @@ -250,11 +250,11 @@ uint ScatterObjectBufferHelper::createRangeGradientUVs(ScatterSeriesRenderCache // Avoid values near gradient texel boundary, as this causes artifacts // with some graphics cards. const float floorY = float(qFloor(y * gradientTextureHeight)); - const float diff = y - floorY; + const float diff = (y * gradientTextureHeight) - floorY; if (diff < yAdjustment) - y += yAdjustment; + y += yAdjustment / gradientTextureHeight; else if (diff > flippedYAdjustment) - y -= yAdjustment; + y -= yAdjustment / gradientTextureHeight; uv.setY(y); int offset = pos * uvsCount; diff --git a/tests/scattertest/scatterchart.cpp b/tests/scattertest/scatterchart.cpp index e5a7acc3..7d625d3f 100644 --- a/tests/scattertest/scatterchart.cpp +++ b/tests/scattertest/scatterchart.cpp @@ -47,6 +47,7 @@ ScatterDataModifier::ScatterDataModifier(Q3DScatter *scatter) m_chart->setAxisX(new QValue3DAxis); m_chart->setAxisY(new QValue3DAxis); m_chart->setAxisZ(new QValue3DAxis); + m_chart->axisY()->setLabelFormat(QStringLiteral("%.7f")); static_cast(m_chart->activeInputHandler())->setZoomAtTargetEnabled(true); createAndAddSeries(); @@ -736,6 +737,9 @@ void ScatterDataModifier::changeBunch() items[i].setY(-1.0f); else if (i == 4) items[i].setY(1.2f); +// else +// items[i].setY(0.1001f - (0.00001f * float(i))); + } m_targetSeries->dataProxy()->setItems(0, items); -- cgit v1.2.3 From 91a9698b80bcb072a5ec3af69515249bac96aff5 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Tue, 14 Oct 2014 14:35:03 +0300 Subject: Simplify context handling at cleanup. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If the context exists at renderer deletion, it is possible that it no longer has valid surface, which means it won't be possible to set it back to current if we change context for deletion cleanup. Since the current context will be one of our shared contexts anyway, there is no need to do a dummy context for cleanup unless the current context is null. Change-Id: Ibabe081742beb975ee848ccb3690703ef5b027a9 Reviewed-by: Mika Salmela Reviewed-by: Tomi Korpipää --- .../engine/abstract3drenderer.cpp | 22 +++++++++------------- .../engine/abstract3drenderer_p.h | 2 -- src/datavisualization/engine/axisrendercache.cpp | 10 ++++++++-- src/datavisualization/engine/axisrendercache_p.h | 1 + tests/qmlmultiwindow/qml/qmlmultiwindow/main.qml | 10 ++++++++-- 5 files changed, 26 insertions(+), 19 deletions(-) diff --git a/src/datavisualization/engine/abstract3drenderer.cpp b/src/datavisualization/engine/abstract3drenderer.cpp index d7bad300..e4141f4c 100644 --- a/src/datavisualization/engine/abstract3drenderer.cpp +++ b/src/datavisualization/engine/abstract3drenderer.cpp @@ -105,8 +105,6 @@ Abstract3DRenderer::Abstract3DRenderer(Abstract3DController *controller) m_reflectionEnabled(false), m_reflectivity(0.5), m_context(0), - m_currentContextAtDelete(0), - m_currentSurfaceAtDelete(0), m_dummySurfaceAtDelete(0) { QObject::connect(m_drawer, &Drawer::drawerChanged, this, &Abstract3DRenderer::updateTextures); @@ -158,6 +156,10 @@ Abstract3DRenderer::~Abstract3DRenderer() delete m_textureHelper; } + m_axisCacheX.clearLabels(); + m_axisCacheY.clearLabels(); + m_axisCacheZ.clearLabels(); + restoreContextAfterDelete(); } @@ -1784,10 +1786,9 @@ void Abstract3DRenderer::queriedGraphPosition(const QMatrix4x4 &projectionViewMa void Abstract3DRenderer::fixContextBeforeDelete() { - m_currentContextAtDelete = QOpenGLContext::currentContext(); - if (m_currentContextAtDelete) - m_currentSurfaceAtDelete = m_currentContextAtDelete->surface(); - if (!m_context.isNull() && m_context.data() != m_currentContextAtDelete + // Only need to fix context if the current context is null. + // Otherwise we expect it to be our shared context, so we can use it for cleanup. + if (!QOpenGLContext::currentContext() && !m_context.isNull() && QThread::currentThread() == this->thread()) { m_dummySurfaceAtDelete = new QWindow(); m_dummySurfaceAtDelete->setSurfaceType(QWindow::OpenGLSurface); @@ -1800,15 +1801,10 @@ void Abstract3DRenderer::fixContextBeforeDelete() void Abstract3DRenderer::restoreContextAfterDelete() { - if (m_currentContextAtDelete && m_currentSurfaceAtDelete - && m_context.data() != m_currentContextAtDelete) { - m_currentContextAtDelete->makeCurrent(m_currentSurfaceAtDelete); - } else if (m_dummySurfaceAtDelete) { + if (m_dummySurfaceAtDelete) m_context->doneCurrent(); - } + delete m_dummySurfaceAtDelete; - m_currentContextAtDelete = 0; - m_currentSurfaceAtDelete = 0; m_dummySurfaceAtDelete = 0; } diff --git a/src/datavisualization/engine/abstract3drenderer_p.h b/src/datavisualization/engine/abstract3drenderer_p.h index 1857278c..38665c65 100644 --- a/src/datavisualization/engine/abstract3drenderer_p.h +++ b/src/datavisualization/engine/abstract3drenderer_p.h @@ -324,8 +324,6 @@ protected: QLocale m_locale; QPointer m_context; // Not owned - QOpenGLContext *m_currentContextAtDelete; // Not owned - QSurface *m_currentSurfaceAtDelete; // Not owned QWindow *m_dummySurfaceAtDelete; private: diff --git a/src/datavisualization/engine/axisrendercache.cpp b/src/datavisualization/engine/axisrendercache.cpp index 2448483e..f467cc76 100644 --- a/src/datavisualization/engine/axisrendercache.cpp +++ b/src/datavisualization/engine/axisrendercache.cpp @@ -44,8 +44,7 @@ AxisRenderCache::AxisRenderCache() AxisRenderCache::~AxisRenderCache() { - foreach (LabelItem *label, m_labelItems) - delete label; + clearLabels(); delete m_formatter; } @@ -175,6 +174,13 @@ void AxisRenderCache::updateTextures() } } +void AxisRenderCache::clearLabels() +{ + m_titleItem.clear(); + for (int i = 0; i < m_labels.size(); i++) + m_labelItems[i]->clear(); +} + int AxisRenderCache::maxLabelWidth(const QStringList &labels) const { int labelWidth = 0; diff --git a/src/datavisualization/engine/axisrendercache_p.h b/src/datavisualization/engine/axisrendercache_p.h index 9fb04837..e32c590e 100644 --- a/src/datavisualization/engine/axisrendercache_p.h +++ b/src/datavisualization/engine/axisrendercache_p.h @@ -108,6 +108,7 @@ public: inline void setTitleFixed(bool fixed) { m_titleFixed = fixed; } void updateTextures(); + void clearLabels(); private: int maxLabelWidth(const QStringList &labels) const; diff --git a/tests/qmlmultiwindow/qml/qmlmultiwindow/main.qml b/tests/qmlmultiwindow/qml/qmlmultiwindow/main.qml index 57d62019..da382e3d 100644 --- a/tests/qmlmultiwindow/qml/qmlmultiwindow/main.qml +++ b/tests/qmlmultiwindow/qml/qmlmultiwindow/main.qml @@ -91,6 +91,13 @@ Rectangle { } } + function destroyWindow() { + if (surfaceWindowObject != null) + surfaceWindowObject.destroy() + } + + Component.onDestruction: destroyWindow() + //! [0] GridLayout { id: gridLayout @@ -114,8 +121,7 @@ Rectangle { running: false repeat: false onTriggered: { - if (surfaceWindowObject != null) - surfaceWindowObject.destroy() + destroyWindow() surfaceWindowObject = Qt.createQmlObject(surfaceWindowStr, mainView) } } -- cgit v1.2.3 From c5d9c5c1973408f0233c9f257a80b484585079c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomi=20Korpip=C3=A4=C3=A4?= Date: Wed, 15 Oct 2014 07:31:09 +0300 Subject: QML tests for theme Task-number: QTRD-3368 Change-Id: I1fa3de637dafcd601490155e29fb36008b1eafcf Reviewed-by: Miikka Heikkinen --- tests/auto/qmltest/qmltest.pro | 5 +- tests/auto/qmltest/theme3d/tst_colorgradient.qml | 89 +++++++++ tests/auto/qmltest/theme3d/tst_theme.qml | 242 +++++++++++++++++++++++ tests/auto/qmltest/theme3d/tst_themecolor.qml | 66 +++++++ 4 files changed, 401 insertions(+), 1 deletion(-) create mode 100644 tests/auto/qmltest/theme3d/tst_colorgradient.qml create mode 100644 tests/auto/qmltest/theme3d/tst_theme.qml create mode 100644 tests/auto/qmltest/theme3d/tst_themecolor.qml diff --git a/tests/auto/qmltest/qmltest.pro b/tests/auto/qmltest/qmltest.pro index a6430862..ea869a2e 100644 --- a/tests/auto/qmltest/qmltest.pro +++ b/tests/auto/qmltest/qmltest.pro @@ -11,7 +11,10 @@ OTHER_FILES += bars3d\tst_basic.qml \ scatter3d\tst_scatterseries.qml \ surface3d\tst_basic.qml \ surface3d\tst_surface.qml \ - surface3d\tst_surfaceseries.qml + surface3d\tst_surfaceseries.qml \ + theme3d\tst_theme.qml \ + theme3d\tst_colorgradient.qml \ + theme3d\tst_themecolor.qml RESOURCES += \ qmltest.qrc diff --git a/tests/auto/qmltest/theme3d/tst_colorgradient.qml b/tests/auto/qmltest/theme3d/tst_colorgradient.qml new file mode 100644 index 00000000..395b6672 --- /dev/null +++ b/tests/auto/qmltest/theme3d/tst_colorgradient.qml @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +import QtQuick 2.0 +import QtDataVisualization 1.2 +import QtTest 1.0 + +Item { + id: top + width: 150 + height: 150 + + ColorGradient { + id: initial + } + + ColorGradient { + id: initialized + stops: [ + ColorGradientStop { color: "blue"; position: 0 }, + ColorGradientStop { color: "white"; position: 0.5 }, + ColorGradientStop { color: "red"; position: 1 } + ] + } + + ColorGradient { + id: change + } + + TestCase { + name: "ColorGradient Initial" + + function test_initial() { + compare(initial.stops.length, 0) + } + } + + TestCase { + name: "ColorGradient Initialized" + + function test_initialized() { + compare(initialized.stops.length, 3) + compare(initialized.stops[0].color, "#0000ff") + compare(initialized.stops[1].color, "#ffffff") + compare(initialized.stops[2].color, "#ff0000") + } + } + + TestCase { + name: "ColorGradient Change" + + ColorGradientStop { id: stop1; color: "blue"; position: 0 } + ColorGradientStop { id: stop2; color: "red"; position: 1.0 } + ColorGradientStop { id: stop3; color: "white"; position: 0.5 } + + function test_change() { + change.stops = [stop1] + compare(change.stops.length, 1) + change.stops = [stop1, stop2] + compare(change.stops.length, 2) + compare(change.stops[0].color, "#0000ff") + change.stops[0].color = "red" + compare(change.stops[0].color, "#ff0000") + compare(change.stops[1].color, "#ff0000") + change.stops = [stop1, stop2, stop3] + compare(change.stops[2].color, "#ffffff") + compare(change.stops.length, 3) + stop2.position = 0.25 + stop3.position = 1.0 + compare(change.stops[1].position, 0.25) + compare(change.stops[2].position, 1.0) + } + } +} diff --git a/tests/auto/qmltest/theme3d/tst_theme.qml b/tests/auto/qmltest/theme3d/tst_theme.qml new file mode 100644 index 00000000..9c1a22f1 --- /dev/null +++ b/tests/auto/qmltest/theme3d/tst_theme.qml @@ -0,0 +1,242 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +import QtQuick 2.0 +import QtDataVisualization 1.2 +import QtTest 1.0 + +Item { + id: top + width: 150 + height: 150 + + Theme3D { + id: initial + } + + ColorGradient { + id: gradient1 + stops: [ + ColorGradientStop { color: "red"; position: 0 }, + ColorGradientStop { color: "blue"; position: 1 } + ] + } + + ColorGradient { + id: gradient2 + stops: [ + ColorGradientStop { color: "green"; position: 0 }, + ColorGradientStop { color: "red"; position: 1 } + ] + } + + ColorGradient { + id: gradient3 + stops: [ + ColorGradientStop { color: "gray"; position: 0 }, + ColorGradientStop { color: "darkgray"; position: 1 } + ] + } + + ThemeColor { + id: color1 + color: "red" + } + + ThemeColor { + id: color2 + color: "blue" + } + + Theme3D { + id: initialized + ambientLightStrength: 0.3 + backgroundColor: "#ff0000" + backgroundEnabled: false + baseColors: [color1, color2] + baseGradients: [gradient1, gradient2] + colorStyle: Theme3D.ColorStyleRangeGradient + font.family: "Arial" + gridEnabled: false + gridLineColor: "#00ff00" + highlightLightStrength: 5.0 + labelBackgroundColor: "#ff00ff" + labelBackgroundEnabled: false + labelBorderEnabled: false + labelTextColor: "#00ffff" + lightColor: "#ffff00" + lightStrength: 2.5 + multiHighlightColor: "#ff00ff" + multiHighlightGradient: gradient3 + singleHighlightColor: "#ff0000" + singleHighlightGradient: gradient3 + type: Theme3D.ThemeQt // Default values will be overwritten by initialized values + windowColor: "#fff00f" + } + + Theme3D { + id: change + } + + TestCase { + name: "Theme3D Initial" + + Text { id: dummy } + + function test_initial() { + compare(initial.ambientLightStrength, 0.25) + compare(initial.backgroundColor, "#000000") + compare(initial.backgroundEnabled, true) + compare(initial.baseColors.length, 1) + compare(initial.baseColors[0].color, "#000000") + compare(initial.baseGradients.length, 1) + compare(initial.baseGradients[0].stops[0].color, "#000000") + compare(initial.baseGradients[0].stops[1].color, "#ffffff") + compare(initial.colorStyle, Theme3D.ColorStyleUniform) + // Initial font needs to be tested like this, as different platforms have different default font (QFont()) + compare(initial.font.family, dummy.font.family) + compare(initial.gridEnabled, true) + compare(initial.gridLineColor, "#ffffff") + compare(initial.highlightLightStrength, 7.5) + compare(initial.labelBackgroundColor, "#a0a0a4") + compare(initial.labelBackgroundEnabled, true) + compare(initial.labelBorderEnabled, true) + compare(initial.labelTextColor, "#ffffff") + compare(initial.lightColor, "#ffffff") + compare(initial.lightStrength, 5) + compare(initial.multiHighlightColor, "#0000ff") + compare(initial.multiHighlightGradient, null) + compare(initial.singleHighlightColor, "#ff0000") + compare(initial.singleHighlightGradient, null) + compare(initial.type, Theme3D.ThemeUserDefined) + compare(initial.windowColor, "#000000") + } + } + + TestCase { + name: "Theme3D Initialized" + + function test_initialized() { + compare(initialized.ambientLightStrength, 0.3) + compare(initialized.backgroundColor, "#ff0000") + compare(initialized.backgroundEnabled, false) + compare(initialized.baseColors.length, 2) + compare(initialized.baseColors[0].color, "#ff0000") + compare(initialized.baseColors[1].color, "#0000ff") + compare(initialized.baseGradients.length, 2) + compare(initialized.baseGradients[0], gradient1) + compare(initialized.baseGradients[1], gradient2) + compare(initialized.colorStyle, Theme3D.ColorStyleRangeGradient) + compare(initialized.font.family, "Arial") + compare(initialized.gridEnabled, false) + compare(initialized.gridLineColor, "#00ff00") + compare(initialized.highlightLightStrength, 5.0) + compare(initialized.labelBackgroundColor, "#ff00ff") + compare(initialized.labelBackgroundEnabled, false) + compare(initialized.labelBorderEnabled, false) + compare(initialized.labelTextColor, "#00ffff") + compare(initialized.lightColor, "#ffff00") + compare(initialized.lightStrength, 2.5) + compare(initialized.multiHighlightColor, "#ff00ff") + compare(initialized.multiHighlightGradient, gradient3) + compare(initialized.singleHighlightColor, "#ff0000") + compare(initialized.singleHighlightGradient, gradient3) + compare(initialized.type, Theme3D.ThemeQt) + compare(initialized.windowColor, "#fff00f") + } + } + + TestCase { + name: "Theme3D Change" + + ThemeColor { + id: color3 + color: "red" + } + + ColorGradient { + id: gradient4 + stops: [ + ColorGradientStop { color: "red"; position: 0 }, + ColorGradientStop { color: "blue"; position: 1 } + ] + } + + function test_1_change() { + change.type = Theme3D.ThemeStoneMoss // Default values will be overwritten by the following sets + change.ambientLightStrength = 0.3 + change.backgroundColor = "#ff0000" + change.backgroundEnabled = false + change.baseColors = [color3, color2] + change.baseGradients = [gradient4, gradient2] + change.colorStyle = Theme3D.ColorStyleObjectGradient + change.font.family = "Arial" + change.gridEnabled = false + change.gridLineColor = "#00ff00" + change.highlightLightStrength = 5.0 + change.labelBackgroundColor = "#ff00ff" + change.labelBackgroundEnabled = false + change.labelBorderEnabled = false + change.labelTextColor = "#00ffff" + change.lightColor = "#ffff00" + change.lightStrength = 2.5 + change.multiHighlightColor = "#ff00ff" + change.multiHighlightGradient = gradient3 + change.singleHighlightColor = "#ff0000" + change.singleHighlightGradient = gradient3 + change.windowColor = "#fff00f" + + compare(change.ambientLightStrength, 0.3) + compare(change.backgroundColor, "#ff0000") + compare(change.backgroundEnabled, false) + compare(change.baseColors.length, 2) + compare(change.baseColors[0].color, "#ff0000") + compare(change.baseColors[1].color, "#0000ff") + compare(change.baseGradients.length, 2) + compare(change.baseGradients[0], gradient4) + compare(change.baseGradients[1], gradient2) + compare(change.colorStyle, Theme3D.ColorStyleObjectGradient) + compare(change.font.family, "Arial") + compare(change.gridEnabled, false) + compare(change.gridLineColor, "#00ff00") + compare(change.highlightLightStrength, 5.0) + compare(change.labelBackgroundColor, "#ff00ff") + compare(change.labelBackgroundEnabled, false) + compare(change.labelBorderEnabled, false) + compare(change.labelTextColor, "#00ffff") + compare(change.lightColor, "#ffff00") + compare(change.lightStrength, 2.5) + compare(change.multiHighlightColor, "#ff00ff") + compare(change.multiHighlightGradient, gradient3) + compare(change.singleHighlightColor, "#ff0000") + compare(change.singleHighlightGradient, gradient3) + compare(change.type, Theme3D.ThemeStoneMoss) + compare(change.windowColor, "#fff00f") + } + + function test_2_change_color() { + color3.color = "white" + compare(change.baseColors[0].color, "#ffffff") + } + + function test_3_change_gradient() { + gradient4.stops[0].color = "black" + compare(change.baseGradients[0].stops[0].color, "#000000") + } + } +} diff --git a/tests/auto/qmltest/theme3d/tst_themecolor.qml b/tests/auto/qmltest/theme3d/tst_themecolor.qml new file mode 100644 index 00000000..d7dd3490 --- /dev/null +++ b/tests/auto/qmltest/theme3d/tst_themecolor.qml @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +import QtQuick 2.0 +import QtDataVisualization 1.2 +import QtTest 1.0 + +Item { + id: top + width: 150 + height: 150 + + ThemeColor { + id: initial + } + + ThemeColor { + id: initialized + color: "red" + } + + ThemeColor { + id: change + } + + TestCase { + name: "ThemeColor Initial" + + function test_initial() { + compare(initial.color, "#000000") + } + } + + TestCase { + name: "ThemeColor Initialized" + + function test_initialized() { + compare(initialized.color, "#ff0000") + } + } + + TestCase { + name: "ThemeColor Change" + + function test_1_change() { + change.color = "blue" + + compare(change.color, "#0000ff") + } + } +} -- cgit v1.2.3 From 733bff1b086e182dba5f9713fefa8995852f0c0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomi=20Korpip=C3=A4=C3=A4?= Date: Wed, 15 Oct 2014 09:42:33 +0300 Subject: Added tests for QML custom items Also added skeleton for remaining QML tests and some inheritance updates to documentation. Task-number: QTRD-3368 Change-Id: I28cb52c4738953b7f281ce91a3764f849643d35a Change-Id: I28cb52c4738953b7f281ce91a3764f849643d35a Reviewed-by: Miikka Heikkinen --- src/datavisualization/data/qcustom3dlabel.cpp | 1 + src/datavisualization/data/qcustom3dvolume.cpp | 1 + tests/auto/qmltest/axis3d/tst_category.qml | 29 ++++ tests/auto/qmltest/axis3d/tst_logvalue.qml | 29 ++++ tests/auto/qmltest/axis3d/tst_value.qml | 29 ++++ tests/auto/qmltest/bars3d/tst_proxy.qml | 29 ++++ tests/auto/qmltest/custom3d/tst_customitem.qml | 106 +++++++++++++++ tests/auto/qmltest/custom3d/tst_customlabel.qml | 134 +++++++++++++++++++ tests/auto/qmltest/custom3d/tst_customvolume.qml | 163 +++++++++++++++++++++++ tests/auto/qmltest/customtexture.jpg | Bin 0 -> 516 bytes tests/auto/qmltest/qmltest.pro | 16 ++- tests/auto/qmltest/qmltest.qrc | 1 + tests/auto/qmltest/scatter3d/tst_proxy.qml | 29 ++++ tests/auto/qmltest/scene3d/tst_camera.qml | 29 ++++ tests/auto/qmltest/scene3d/tst_light.qml | 29 ++++ tests/auto/qmltest/scene3d/tst_scene.qml | 29 ++++ tests/auto/qmltest/surface3d/tst_proxy.qml | 29 ++++ 17 files changed, 682 insertions(+), 1 deletion(-) create mode 100644 tests/auto/qmltest/axis3d/tst_category.qml create mode 100644 tests/auto/qmltest/axis3d/tst_logvalue.qml create mode 100644 tests/auto/qmltest/axis3d/tst_value.qml create mode 100644 tests/auto/qmltest/bars3d/tst_proxy.qml create mode 100644 tests/auto/qmltest/custom3d/tst_customitem.qml create mode 100644 tests/auto/qmltest/custom3d/tst_customlabel.qml create mode 100644 tests/auto/qmltest/custom3d/tst_customvolume.qml create mode 100644 tests/auto/qmltest/customtexture.jpg create mode 100644 tests/auto/qmltest/scatter3d/tst_proxy.qml create mode 100644 tests/auto/qmltest/scene3d/tst_camera.qml create mode 100644 tests/auto/qmltest/scene3d/tst_light.qml create mode 100644 tests/auto/qmltest/scene3d/tst_scene.qml create mode 100644 tests/auto/qmltest/surface3d/tst_proxy.qml diff --git a/src/datavisualization/data/qcustom3dlabel.cpp b/src/datavisualization/data/qcustom3dlabel.cpp index 85a37e2d..bb5d71d0 100644 --- a/src/datavisualization/data/qcustom3dlabel.cpp +++ b/src/datavisualization/data/qcustom3dlabel.cpp @@ -44,6 +44,7 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION * \since QtDataVisualization 1.1 * \ingroup datavisualization_qml * \instantiates QCustom3DLabel + * \inherits Custom3DItem * \brief The Custom3DLabel type is for creating custom labels to be added to a graph. * * This type is for creating custom labels to be added to a graph. You can set text, font, diff --git a/src/datavisualization/data/qcustom3dvolume.cpp b/src/datavisualization/data/qcustom3dvolume.cpp index 563af31a..f8287a81 100644 --- a/src/datavisualization/data/qcustom3dvolume.cpp +++ b/src/datavisualization/data/qcustom3dvolume.cpp @@ -52,6 +52,7 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION * \since QtDataVisualization 1.2 * \ingroup datavisualization_qml * \instantiates QCustom3DVolume + * \inherits Custom3DItem * \brief The Custom3DVolume type is for creating volume rendered objects to be added to a graph. * * This class is for creating volume rendered objects to be added to a graph. A volume rendered diff --git a/tests/auto/qmltest/axis3d/tst_category.qml b/tests/auto/qmltest/axis3d/tst_category.qml new file mode 100644 index 00000000..98e1779f --- /dev/null +++ b/tests/auto/qmltest/axis3d/tst_category.qml @@ -0,0 +1,29 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +import QtQuick 2.0 +import QtDataVisualization 1.2 +import QtTest 1.0 + +Item { + id: top + height: 150 + width: 150 + + // TODO: Add tests for CategoryAxis3D +} diff --git a/tests/auto/qmltest/axis3d/tst_logvalue.qml b/tests/auto/qmltest/axis3d/tst_logvalue.qml new file mode 100644 index 00000000..7c6dc83c --- /dev/null +++ b/tests/auto/qmltest/axis3d/tst_logvalue.qml @@ -0,0 +1,29 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +import QtQuick 2.0 +import QtDataVisualization 1.2 +import QtTest 1.0 + +Item { + id: top + height: 150 + width: 150 + + // TODO: Add tests for LogValueAxis3DFormatter +} diff --git a/tests/auto/qmltest/axis3d/tst_value.qml b/tests/auto/qmltest/axis3d/tst_value.qml new file mode 100644 index 00000000..fa70608b --- /dev/null +++ b/tests/auto/qmltest/axis3d/tst_value.qml @@ -0,0 +1,29 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +import QtQuick 2.0 +import QtDataVisualization 1.2 +import QtTest 1.0 + +Item { + id: top + height: 150 + width: 150 + + // TODO: Add tests for ValueAxis3D +} diff --git a/tests/auto/qmltest/bars3d/tst_proxy.qml b/tests/auto/qmltest/bars3d/tst_proxy.qml new file mode 100644 index 00000000..7c117ce4 --- /dev/null +++ b/tests/auto/qmltest/bars3d/tst_proxy.qml @@ -0,0 +1,29 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +import QtQuick 2.0 +import QtDataVisualization 1.2 +import QtTest 1.0 + +Item { + id: top + height: 150 + width: 150 + + // TODO: Add tests for ItemModelBarDataProxy +} diff --git a/tests/auto/qmltest/custom3d/tst_customitem.qml b/tests/auto/qmltest/custom3d/tst_customitem.qml new file mode 100644 index 00000000..ee3d10fc --- /dev/null +++ b/tests/auto/qmltest/custom3d/tst_customitem.qml @@ -0,0 +1,106 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +import QtQuick 2.0 +import QtDataVisualization 1.2 +import QtTest 1.0 + +Item { + id: top + width: 150 + height: 150 + + Custom3DItem { + id: initial + } + + Custom3DItem { + id: initialized + meshFile: ":\customitem.obj" + position: Qt.vector3d(1.0, 0.5, 1.0) + positionAbsolute: true + rotation: Qt.quaternion(1, 0.5, 0, 0) + scaling: Qt.vector3d(0.2, 0.2, 0.2) + scalingAbsolute: false + shadowCasting: false + textureFile: ":\customtexture.jpg" + visible: false + } + + Custom3DItem { + id: change + } + + TestCase { + name: "Custom3DItem Initial" + + function test_initial() { + compare(initial.meshFile, "") + compare(initial.position, Qt.vector3d(0.0, 0.0, 0.0)) + compare(initial.positionAbsolute, false) + compare(initial.rotation, Qt.quaternion(0, 0, 0, 0)) + compare(initial.scaling, Qt.vector3d(0.1, 0.1, 0.1)) + compare(initial.scalingAbsolute, true) + compare(initial.shadowCasting, true) + compare(initial.textureFile, "") + compare(initial.visible, true) + } + } + + TestCase { + name: "Custom3DItem Initialized" + + function test_initialized() { + compare(initialized.meshFile, ":\customitem.obj") + compare(initialized.position, Qt.vector3d(1.0, 0.5, 1.0)) + compare(initialized.positionAbsolute, true) + compare(initialized.rotation, Qt.quaternion(1, 0.5, 0, 0)) + compare(initialized.scaling, Qt.vector3d(0.2, 0.2, 0.2)) + compare(initialized.scalingAbsolute, false) + compare(initialized.shadowCasting, false) + compare(initialized.textureFile, ":\customtexture.jpg") + compare(initialized.visible, false) + } + } + + TestCase { + name: "Custom3DItem Change" + + function test_change() { + change.meshFile = ":\customitem.obj" + change.position = Qt.vector3d(1.0, 0.5, 1.0) + change.positionAbsolute = true + change.rotation = Qt.quaternion(1, 0.5, 0, 0) + change.scaling = Qt.vector3d(0.2, 0.2, 0.2) + change.scalingAbsolute = false + change.shadowCasting = false + change.textureFile = ":\customtexture.jpg" + change.visible = false + + compare(change.meshFile, ":\customitem.obj") + compare(change.position, Qt.vector3d(1.0, 0.5, 1.0)) + compare(change.positionAbsolute, true) + compare(change.rotation, Qt.quaternion(1, 0.5, 0, 0)) + compare(change.scaling, Qt.vector3d(0.2, 0.2, 0.2)) + compare(change.scalingAbsolute, false) + compare(change.shadowCasting, false) + compare(change.textureFile, ":\customtexture.jpg") + compare(change.visible, false) + } + } +} diff --git a/tests/auto/qmltest/custom3d/tst_customlabel.qml b/tests/auto/qmltest/custom3d/tst_customlabel.qml new file mode 100644 index 00000000..acac1b63 --- /dev/null +++ b/tests/auto/qmltest/custom3d/tst_customlabel.qml @@ -0,0 +1,134 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +import QtQuick 2.0 +import QtDataVisualization 1.2 +import QtTest 1.0 + +Item { + id: top + width: 150 + height: 150 + + Custom3DLabel { + id: initial + } + + Custom3DLabel { + id: initialized + backgroundColor: "red" + backgroundEnabled: false + borderEnabled: false + facingCamera: true + font.family: "Times New Roman" + text: "test label" + textColor: "blue" + + position: Qt.vector3d(1.0, 0.5, 1.0) + positionAbsolute: true + rotation: Qt.quaternion(1, 0.5, 0, 0) + scaling: Qt.vector3d(0.2, 0.2, 0.2) + shadowCasting: true + visible: false + } + + Custom3DLabel { + id: change + } + + TestCase { + name: "Custom3DLabel Initial" + + function test_initial() { + compare(initial.backgroundColor, "#a0a0a4") + compare(initial.backgroundEnabled, true) + compare(initial.borderEnabled, true) + compare(initial.facingCamera, false) + compare(initial.font.family, "Arial") + compare(initial.text, "") + compare(initial.textColor, "#ffffff") + + compare(initial.meshFile, ":/defaultMeshes/plane") + compare(initial.position, Qt.vector3d(0.0, 0.0, 0.0)) + compare(initial.positionAbsolute, false) + compare(initial.rotation, Qt.quaternion(0, 0, 0, 0)) + compare(initial.scaling, Qt.vector3d(0.1, 0.1, 0.1)) + compare(initial.scalingAbsolute, true) + compare(initial.shadowCasting, false) + compare(initial.textureFile, "") + compare(initial.visible, true) + } + } + + TestCase { + name: "Custom3DLabel Initialized" + + function test_initialized() { + compare(initialized.backgroundColor, "#ff0000") + compare(initialized.backgroundEnabled, false) + compare(initialized.borderEnabled, false) + compare(initialized.facingCamera, true) + compare(initialized.font.family, "Times New Roman") + compare(initialized.text, "test label") + compare(initialized.textColor, "#0000ff") + + compare(initialized.position, Qt.vector3d(1.0, 0.5, 1.0)) + compare(initialized.positionAbsolute, true) + compare(initialized.rotation, Qt.quaternion(1, 0.5, 0, 0)) + compare(initialized.scaling, Qt.vector3d(0.2, 0.2, 0.2)) + compare(initialized.shadowCasting, true) + compare(initialized.visible, false) + } + } + + TestCase { + name: "Custom3DLabel Change" + + function test_change() { + change.backgroundColor = "red" + change.backgroundEnabled = false + change.borderEnabled = false + change.facingCamera = true + change.font.family = "Times New Roman" + change.text = "test label" + change.textColor = "blue" + + change.position = Qt.vector3d(1.0, 0.5, 1.0) + change.positionAbsolute = true + change.rotation = Qt.quaternion(1, 0.5, 0, 0) + change.scaling = Qt.vector3d(0.2, 0.2, 0.2) + change.shadowCasting = true + change.visible = false + + compare(change.backgroundColor, "#ff0000") + compare(change.backgroundEnabled, false) + compare(change.borderEnabled, false) + compare(change.facingCamera, true) + compare(change.font.family, "Times New Roman") + compare(change.text, "test label") + compare(change.textColor, "#0000ff") + + compare(change.position, Qt.vector3d(1.0, 0.5, 1.0)) + compare(change.positionAbsolute, true) + compare(change.rotation, Qt.quaternion(1, 0.5, 0, 0)) + compare(change.scaling, Qt.vector3d(0.2, 0.2, 0.2)) + compare(change.shadowCasting, true) + compare(change.visible, false) + } + } +} diff --git a/tests/auto/qmltest/custom3d/tst_customvolume.qml b/tests/auto/qmltest/custom3d/tst_customvolume.qml new file mode 100644 index 00000000..89717d5d --- /dev/null +++ b/tests/auto/qmltest/custom3d/tst_customvolume.qml @@ -0,0 +1,163 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +import QtQuick 2.0 +import QtDataVisualization 1.2 +import QtTest 1.0 + +Item { + id: top + width: 150 + height: 150 + + Custom3DVolume { + id: initial + } + + Custom3DVolume { + id: initialized + alphaMultiplier: 0.1 + drawSliceFrames: true + drawSlices: true + preserveOpacity: false + sliceFrameColor: "red" + sliceFrameGaps: Qt.vector3d(2.0, 2.0, 2.0) + sliceFrameThicknesses: Qt.vector3d(2.0, 2.0, 2.0) + sliceFrameWidths: Qt.vector3d(2.0, 2.0, 2.0) + sliceIndexX: 0 + sliceIndexY: 0 + sliceIndexZ: 0 + useHighDefShader: false + + position: Qt.vector3d(1.0, 0.5, 1.0) + positionAbsolute: true + rotation: Qt.quaternion(1, 0.5, 0, 0) + scaling: Qt.vector3d(0.2, 0.2, 0.2) + scalingAbsolute: false + shadowCasting: false + visible: false + } + + Custom3DVolume { + id: change + } + + TestCase { + name: "Custom3DVolume Initial" + + function test_initial() { + compare(initial.alphaMultiplier, 1.0) + compare(initial.drawSliceFrames, false) + compare(initial.drawSlices, false) + compare(initial.preserveOpacity, true) + compare(initial.sliceFrameColor, "#000000") + compare(initial.sliceFrameGaps, Qt.vector3d(0.01, 0.01, 0.01)) + compare(initial.sliceFrameThicknesses, Qt.vector3d(0.01, 0.01, 0.01)) + compare(initial.sliceFrameWidths, Qt.vector3d(0.01, 0.01, 0.01)) + compare(initial.sliceIndexX, -1) + compare(initial.sliceIndexY, -1) + compare(initial.sliceIndexZ, -1) + compare(initial.useHighDefShader, true) + + compare(initial.meshFile, ":/defaultMeshes/barFull") + compare(initial.position, Qt.vector3d(0.0, 0.0, 0.0)) + compare(initial.positionAbsolute, false) + compare(initial.rotation, Qt.quaternion(0, 0, 0, 0)) + compare(initial.scaling, Qt.vector3d(0.1, 0.1, 0.1)) + compare(initial.scalingAbsolute, true) + compare(initial.shadowCasting, true) + compare(initial.textureFile, "") + compare(initial.visible, true) + } + } + + TestCase { + name: "Custom3DVolume Initialized" + + function test_initialized() { + compare(initialized.alphaMultiplier, 0.1) + compare(initialized.drawSliceFrames, true) + compare(initialized.drawSlices, true) + compare(initialized.preserveOpacity, false) + compare(initialized.sliceFrameColor, "#ff0000") + compare(initialized.sliceFrameGaps, Qt.vector3d(2.0, 2.0, 2.0)) + compare(initialized.sliceFrameThicknesses, Qt.vector3d(2.0, 2.0, 2.0)) + compare(initialized.sliceFrameWidths, Qt.vector3d(2.0, 2.0, 2.0)) + compare(initialized.sliceIndexX, 0) + compare(initialized.sliceIndexY, 0) + compare(initialized.sliceIndexZ, 0) + compare(initialized.useHighDefShader, false) + + compare(initialized.position, Qt.vector3d(1.0, 0.5, 1.0)) + compare(initialized.positionAbsolute, true) + compare(initialized.rotation, Qt.quaternion(1, 0.5, 0, 0)) + compare(initialized.scaling, Qt.vector3d(0.2, 0.2, 0.2)) + compare(initialized.scalingAbsolute, false) + compare(initialized.shadowCasting, false) + compare(initialized.visible, false) + } + } + + TestCase { + name: "Custom3DVolume Change" + + function test_change() { + change.alphaMultiplier = 0.1 + change.drawSliceFrames = true + change.drawSlices = true + change.preserveOpacity = false + change.sliceFrameColor = "red" + change.sliceFrameGaps = Qt.vector3d(2.0, 2.0, 2.0) + change.sliceFrameThicknesses = Qt.vector3d(2.0, 2.0, 2.0) + change.sliceFrameWidths = Qt.vector3d(2.0, 2.0, 2.0) + change.sliceIndexX = 0 + change.sliceIndexY = 0 + change.sliceIndexZ = 0 + change.useHighDefShader = false + + change.position = Qt.vector3d(1.0, 0.5, 1.0) + change.positionAbsolute = true + change.rotation = Qt.quaternion(1, 0.5, 0, 0) + change.scaling = Qt.vector3d(0.2, 0.2, 0.2) + change.scalingAbsolute = false + change.shadowCasting = false + change.visible = false + + compare(change.alphaMultiplier, 0.1) + compare(change.drawSliceFrames, true) + compare(change.drawSlices, true) + compare(change.preserveOpacity, false) + compare(change.sliceFrameColor, "#ff0000") + compare(change.sliceFrameGaps, Qt.vector3d(2.0, 2.0, 2.0)) + compare(change.sliceFrameThicknesses, Qt.vector3d(2.0, 2.0, 2.0)) + compare(change.sliceFrameWidths, Qt.vector3d(2.0, 2.0, 2.0)) + compare(change.sliceIndexX, 0) + compare(change.sliceIndexY, 0) + compare(change.sliceIndexZ, 0) + compare(change.useHighDefShader, false) + + compare(change.position, Qt.vector3d(1.0, 0.5, 1.0)) + compare(change.positionAbsolute, true) + compare(change.rotation, Qt.quaternion(1, 0.5, 0, 0)) + compare(change.scaling, Qt.vector3d(0.2, 0.2, 0.2)) + compare(change.scalingAbsolute, false) + compare(change.shadowCasting, false) + compare(change.visible, false) + } + } +} diff --git a/tests/auto/qmltest/customtexture.jpg b/tests/auto/qmltest/customtexture.jpg new file mode 100644 index 00000000..2580f5bd Binary files /dev/null and b/tests/auto/qmltest/customtexture.jpg differ diff --git a/tests/auto/qmltest/qmltest.pro b/tests/auto/qmltest/qmltest.pro index ea869a2e..69d39def 100644 --- a/tests/auto/qmltest/qmltest.pro +++ b/tests/auto/qmltest/qmltest.pro @@ -6,15 +6,29 @@ SOURCES += tst_qmltest.cpp OTHER_FILES += bars3d\tst_basic.qml \ bars3d\tst_bars.qml \ bars3d\tst_barseries.qml \ + #bars3d\tst_proxy.qml \ scatter3d\tst_basic.qml \ scatter3d\tst_scatter.qml \ scatter3d\tst_scatterseries.qml \ + #scatter3d\tst_proxy.qml \ surface3d\tst_basic.qml \ surface3d\tst_surface.qml \ surface3d\tst_surfaceseries.qml \ + #surface3d\tst_proxy.qml \ theme3d\tst_theme.qml \ theme3d\tst_colorgradient.qml \ - theme3d\tst_themecolor.qml + theme3d\tst_themecolor.qml \ + custom3d\tst_customitem.qml \ + custom3d\tst_customlabel.qml \ + custom3d\tst_customvolume.qml #\ +# scene3d\tst_scene.qml \ +# scene3d\tst_camera.qml \ +# scene3d\tst_light.qml \ +# axis3d\tst_category.qml \ +# axis3d\tst_value.qml \ +# axis3d\tst_logvalue.qml + +# TODO: Check new QML types (and properties) in 1.2 (only customvolume added for now) RESOURCES += \ qmltest.qrc diff --git a/tests/auto/qmltest/qmltest.qrc b/tests/auto/qmltest/qmltest.qrc index 69def7f8..61f19086 100644 --- a/tests/auto/qmltest/qmltest.qrc +++ b/tests/auto/qmltest/qmltest.qrc @@ -1,5 +1,6 @@ customitem.obj + customtexture.jpg diff --git a/tests/auto/qmltest/scatter3d/tst_proxy.qml b/tests/auto/qmltest/scatter3d/tst_proxy.qml new file mode 100644 index 00000000..4476bf68 --- /dev/null +++ b/tests/auto/qmltest/scatter3d/tst_proxy.qml @@ -0,0 +1,29 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +import QtQuick 2.0 +import QtDataVisualization 1.2 +import QtTest 1.0 + +Item { + id: top + height: 150 + width: 150 + + // TODO: Add tests for ItemModelScatterDataProxy +} diff --git a/tests/auto/qmltest/scene3d/tst_camera.qml b/tests/auto/qmltest/scene3d/tst_camera.qml new file mode 100644 index 00000000..560f5583 --- /dev/null +++ b/tests/auto/qmltest/scene3d/tst_camera.qml @@ -0,0 +1,29 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +import QtQuick 2.0 +import QtDataVisualization 1.2 +import QtTest 1.0 + +Item { + id: top + height: 150 + width: 150 + + // TODO: Add tests for Camera3D +} diff --git a/tests/auto/qmltest/scene3d/tst_light.qml b/tests/auto/qmltest/scene3d/tst_light.qml new file mode 100644 index 00000000..55a18a56 --- /dev/null +++ b/tests/auto/qmltest/scene3d/tst_light.qml @@ -0,0 +1,29 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +import QtQuick 2.0 +import QtDataVisualization 1.2 +import QtTest 1.0 + +Item { + id: top + height: 150 + width: 150 + + // TODO: Add tests for Light3D +} diff --git a/tests/auto/qmltest/scene3d/tst_scene.qml b/tests/auto/qmltest/scene3d/tst_scene.qml new file mode 100644 index 00000000..348ae646 --- /dev/null +++ b/tests/auto/qmltest/scene3d/tst_scene.qml @@ -0,0 +1,29 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +import QtQuick 2.0 +import QtDataVisualization 1.2 +import QtTest 1.0 + +Item { + id: top + height: 150 + width: 150 + + // TODO: Add tests for Scene3D +} diff --git a/tests/auto/qmltest/surface3d/tst_proxy.qml b/tests/auto/qmltest/surface3d/tst_proxy.qml new file mode 100644 index 00000000..25753a3f --- /dev/null +++ b/tests/auto/qmltest/surface3d/tst_proxy.qml @@ -0,0 +1,29 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +import QtQuick 2.0 +import QtDataVisualization 1.2 +import QtTest 1.0 + +Item { + id: top + height: 150 + width: 150 + + // TODO: Add tests for ItemModelSurfaceDataProxy and HeightMapSurfaceDataProxy +} -- cgit v1.2.3 From f5ee23bb1ff27dbd3cfaa714bfd907e8b9e17d2b Mon Sep 17 00:00:00 2001 From: Mika Salmela Date: Tue, 14 Oct 2014 17:04:47 +0300 Subject: Small cleanups for surface MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Unnecessary modelMatrix multiplications removed. Intentional whitespace removal. Change-Id: I78b8e6f984d44baf7015c9551701b04cb869fc8b Reviewed-by: Tomi Korpipää Reviewed-by: Miikka Heikkinen --- src/datavisualization/engine/surface3drenderer.cpp | 19 ++++++------------- src/datavisualization/utils/surfaceobject.cpp | 2 +- tests/surfacetest/graphmodifier.cpp | 1 - 3 files changed, 7 insertions(+), 15 deletions(-) diff --git a/src/datavisualization/engine/surface3drenderer.cpp b/src/datavisualization/engine/surface3drenderer.cpp index efecb0e1..e39c986c 100644 --- a/src/datavisualization/engine/surface3drenderer.cpp +++ b/src/datavisualization/engine/surface3drenderer.cpp @@ -1210,12 +1210,9 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) SurfaceObject *object = cache->surfaceObject(); if (object->indexCount() && cache->surfaceVisible() && cache->isVisible() && cache->sampleSpace().width() >= 2 && cache->sampleSpace().height() >= 2) { - QMatrix4x4 modelMatrix; - QMatrix4x4 MVPMatrix; - - MVPMatrix = depthProjectionViewMatrix * modelMatrix; - cache->setMVPMatrix(MVPMatrix); - m_depthShader->setUniformValue(m_depthShader->MVP(), MVPMatrix); + // No translation nor scaling for surfaces, therefore no modelMatrix + // Use directly projectionViewMatrix + m_depthShader->setUniformValue(m_depthShader->MVP(), depthProjectionViewMatrix); // 1st attribute buffer : vertices glEnableVertexAttribArray(m_depthShader->posAtt()); @@ -1291,11 +1288,7 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) foreach (SeriesRenderCache *baseCache, m_renderCacheList) { SurfaceSeriesRenderCache *cache = static_cast(baseCache); if (cache->surfaceObject()->indexCount() && cache->renderable()) { - QMatrix4x4 modelMatrix; - QMatrix4x4 MVPMatrix; - - MVPMatrix = projectionViewMatrix * modelMatrix; - m_selectionShader->setUniformValue(m_selectionShader->MVP(), MVPMatrix); + m_selectionShader->setUniformValue(m_selectionShader->MVP(), projectionViewMatrix); cache->surfaceObject()->activateSurfaceTexture(false); @@ -1348,9 +1341,9 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) QMatrix4x4 itModelMatrix; #ifdef SHOW_DEPTH_TEXTURE_SCENE - MVPMatrix = depthProjectionViewMatrix * modelMatrix; + MVPMatrix = depthProjectionViewMatrix; #else - MVPMatrix = projectionViewMatrix * modelMatrix; + MVPMatrix = projectionViewMatrix; #endif cache->setMVPMatrix(MVPMatrix); diff --git a/src/datavisualization/utils/surfaceobject.cpp b/src/datavisualization/utils/surfaceobject.cpp index 86cd2789..5498c8b3 100644 --- a/src/datavisualization/utils/surfaceobject.cpp +++ b/src/datavisualization/utils/surfaceobject.cpp @@ -110,7 +110,7 @@ void SurfaceObject::setUpSmoothData(const QSurfaceDataArray &dataArray, const QR totalIndex = 0; if ((m_dataDimension == BothAscending) || (m_dataDimension == XDescending)) { - for (int row = 0; row < rowLimit; row ++) + for (int row = 0; row < rowLimit; row++) createSmoothNormalBodyLine(totalIndex, row * m_columns); createSmoothNormalUpperLine(totalIndex); } else { // BothDescending || ZDescending diff --git a/tests/surfacetest/graphmodifier.cpp b/tests/surfacetest/graphmodifier.cpp index 5d8183b0..7192f0dc 100644 --- a/tests/surfacetest/graphmodifier.cpp +++ b/tests/surfacetest/graphmodifier.cpp @@ -170,7 +170,6 @@ void GraphModifier::fillSeries() (*newRow[s])[j].setPosition(QVector3D(x, y, z)); } } - qDebug() << newRow[0]->at(0).z(); *dataArray1 << newRow[0]; *dataArray2 << newRow[1]; *dataArray3 << newRow[2]; -- cgit v1.2.3 From 0eb5da28b49d770bbbe7b3e752df2fd2f66c4806 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Wed, 15 Oct 2014 10:53:27 +0300 Subject: Fix the bug introduced in previous fix. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It's not enough to just clear the label items upon axis cache destruction, they need to be deleted, too. Change-Id: I63bdd01f9b328491107292f42cca6aa2ba7b4434 Reviewed-by: Tomi Korpipää --- src/datavisualization/engine/axisrendercache.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/datavisualization/engine/axisrendercache.cpp b/src/datavisualization/engine/axisrendercache.cpp index f467cc76..02e3b7f6 100644 --- a/src/datavisualization/engine/axisrendercache.cpp +++ b/src/datavisualization/engine/axisrendercache.cpp @@ -44,7 +44,9 @@ AxisRenderCache::AxisRenderCache() AxisRenderCache::~AxisRenderCache() { - clearLabels(); + foreach (LabelItem *label, m_labelItems) + delete label; + m_titleItem.clear(); delete m_formatter; } -- cgit v1.2.3 From 3607a9c92b6544dfeab59ac8d83ba1517e7cdc93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomi=20Korpip=C3=A4=C3=A4?= Date: Wed, 15 Oct 2014 11:40:38 +0300 Subject: Added test for QML scene types Also added missing version 1.2 properties to previously done tests. Task-number: QTRD-3368 Change-Id: Ifa5197cecc9bb95ca288a44ebcbd6fbcc0bf1e7d Reviewed-by: Miikka Heikkinen --- tests/auto/qmltest/qmltest.pro | 10 +-- tests/auto/qmltest/scene3d/tst_camera.qml | 94 +++++++++++++++++++++- tests/auto/qmltest/scene3d/tst_light.qml | 38 ++++++++- tests/auto/qmltest/scene3d/tst_scene.qml | 85 ++++++++++++++++++- tests/auto/qmltest/surface3d/tst_surfaceseries.qml | 4 + tests/auto/qmltest/theme3d/tst_themecolor.qml | 2 +- 6 files changed, 223 insertions(+), 10 deletions(-) diff --git a/tests/auto/qmltest/qmltest.pro b/tests/auto/qmltest/qmltest.pro index 69d39def..f660f759 100644 --- a/tests/auto/qmltest/qmltest.pro +++ b/tests/auto/qmltest/qmltest.pro @@ -20,15 +20,15 @@ OTHER_FILES += bars3d\tst_basic.qml \ theme3d\tst_themecolor.qml \ custom3d\tst_customitem.qml \ custom3d\tst_customlabel.qml \ - custom3d\tst_customvolume.qml #\ -# scene3d\tst_scene.qml \ -# scene3d\tst_camera.qml \ -# scene3d\tst_light.qml \ + custom3d\tst_customvolume.qml \ + scene3d\tst_scene.qml \ + scene3d\tst_camera.qml \ + scene3d\tst_light.qml #\ # axis3d\tst_category.qml \ # axis3d\tst_value.qml \ # axis3d\tst_logvalue.qml -# TODO: Check new QML types (and properties) in 1.2 (only customvolume added for now) +# TODO: Check new QML types in 1.2 RESOURCES += \ qmltest.qrc diff --git a/tests/auto/qmltest/scene3d/tst_camera.qml b/tests/auto/qmltest/scene3d/tst_camera.qml index 560f5583..c29de0c7 100644 --- a/tests/auto/qmltest/scene3d/tst_camera.qml +++ b/tests/auto/qmltest/scene3d/tst_camera.qml @@ -22,8 +22,98 @@ import QtTest 1.0 Item { id: top - height: 150 width: 150 + height: 150 + + Camera3D { + id: initial + } + + Camera3D { + id: initialized + maxZoomLevel: 1000.0 + minZoomLevel: 100.0 + target: Qt.vector3d(1.0, -1.0, 1.0) + wrapXRotation: false + wrapYRotation: true + xRotation: 30.0 + yRotation: 30.0 + zoomLevel: 500.0 + } + + Camera3D { + id: change + } + + TestCase { + name: "Camera3D Initial" + + function test_initial() { + compare(initial.cameraPreset, Camera3D.CameraPresetNone) + compare(initial.maxZoomLevel, 500.0) + compare(initial.minZoomLevel, 10.0) + compare(initial.target, Qt.vector3d(0.0, 0.0, 0.0)) + compare(initial.wrapXRotation, true) + compare(initial.wrapYRotation, false) + compare(initial.xRotation, 0.0) + compare(initial.yRotation, 0.0) + compare(initial.zoomLevel, 100.0) + } + } + + TestCase { + name: "Camera3D Initialized" + + function test_initialized() { + compare(initialized.maxZoomLevel, 1000.0) + compare(initialized.minZoomLevel, 100.0) + compare(initialized.target, Qt.vector3d(1.0, -1.0, 1.0)) + compare(initialized.wrapXRotation, false) + compare(initialized.wrapYRotation, true) + compare(initialized.xRotation, 30.0) + compare(initialized.yRotation, 30.0) + compare(initialized.zoomLevel, 500.0) + } + } + + TestCase { + name: "Camera3D Change" + + function test_1_change() { + change.cameraPreset = Camera3D.CameraPresetBehind // Will be overridden by the the following sets + change.maxZoomLevel = 1000.0 + change.minZoomLevel = 100.0 + change.target = Qt.vector3d(1.0, -1.0, 1.0) + change.wrapXRotation = false + change.wrapYRotation = true + change.xRotation = 30.0 + change.yRotation = 30.0 + change.zoomLevel = 500.0 + + compare(change.cameraPreset, Camera3D.CameraPresetNone) + compare(change.maxZoomLevel, 1000.0) + compare(change.minZoomLevel, 100.0) + compare(change.target, Qt.vector3d(1.0, -1.0, 1.0)) + compare(change.wrapXRotation, false) + compare(change.wrapYRotation, true) + compare(change.xRotation, 30.0) + compare(change.yRotation, 30.0) + compare(change.zoomLevel, 500.0) + } + + function test_2_change_preset() { + change.cameraPreset = Camera3D.CameraPresetBehind // Sets target and rotations + + compare(change.cameraPreset, Camera3D.CameraPresetBehind) + compare(change.maxZoomLevel, 1000.0) + compare(change.minZoomLevel, 100.0) + compare(change.target, Qt.vector3d(0.0, 0.0, 0.0)) + compare(change.wrapXRotation, false) + compare(change.wrapYRotation, true) + compare(change.xRotation, 180.0) + compare(change.yRotation, 22.5) + compare(change.zoomLevel, 500.0) + } - // TODO: Add tests for Camera3D + } } diff --git a/tests/auto/qmltest/scene3d/tst_light.qml b/tests/auto/qmltest/scene3d/tst_light.qml index 55a18a56..d9fa282b 100644 --- a/tests/auto/qmltest/scene3d/tst_light.qml +++ b/tests/auto/qmltest/scene3d/tst_light.qml @@ -25,5 +25,41 @@ Item { height: 150 width: 150 - // TODO: Add tests for Light3D + // TODO: Has no adjustable properties yet. + // Keeping this as a placeholder for future implementations (QTRD-2406) + /* + Light3D { + id: initial + } + + Light3D { + id: initialized + } + + + Light3D { + id: change + } + + TestCase { + name: "Light3D Initial" + + function test_initial() { + } + } + + TestCase { + name: "Light3D Initialized" + + function test_initialized() { + } + } + + TestCase { + name: "Light3D Change" + + function test_change() { + } + } + */ } diff --git a/tests/auto/qmltest/scene3d/tst_scene.qml b/tests/auto/qmltest/scene3d/tst_scene.qml index 348ae646..8d497b5c 100644 --- a/tests/auto/qmltest/scene3d/tst_scene.qml +++ b/tests/auto/qmltest/scene3d/tst_scene.qml @@ -25,5 +25,88 @@ Item { height: 150 width: 150 - // TODO: Add tests for Scene3D + // Scene3D is uncreatable, so it needs to be accessed via a graph + Bars3D { + id: initial + } + + Bars3D { + id: initialized + scene.activeCamera: Camera3D { zoomLevel: 200 } + scene.devicePixelRatio: 2.0 + //scene.graphPositionQuery: Qt.point(0, 0) // TODO: Unusable until QTBUG-40043 is fixed + scene.primarySubViewport: Qt.rect(0, 0, 50, 50) + scene.secondarySubViewport: Qt.rect(50, 50, 100, 100) + scene.secondarySubviewOnTop: false + scene.selectionQueryPosition: Qt.point(0, 0) + scene.slicingActive: true + } + + Bars3D { + id: change + } + + TestCase { + name: "Scene3D Initial" + + function test_initial() { + verify(initial.scene.activeCamera) + verify(initial.scene.activeLight) + compare(initial.scene.devicePixelRatio, 1.0) + compare(initial.scene.graphPositionQuery, Qt.point(-1, -1)) + compare(initial.scene.invalidSelectionPoint, Qt.point(-1, -1)) + compare(initial.scene.primarySubViewport, Qt.rect(0, 0, 0, 0)) + compare(initial.scene.secondarySubViewport, Qt.rect(0, 0, 0, 0)) + compare(initial.scene.secondarySubviewOnTop, true) + compare(initial.scene.selectionQueryPosition, Qt.point(-1, -1)) + compare(initial.scene.slicingActive, false) + compare(initial.scene.viewport, Qt.rect(0, 0, 0, 0)) + } + } + + TestCase { + name: "Scene3D Initialized" + + function test_initialized() { + compare(initialized.scene.activeCamera.zoomLevel, 200) + compare(initialized.scene.devicePixelRatio, 2.0) + //compare(initialized.scene.graphPositionQuery, Qt.point(0, 0)) // TODO: Unusable until QTBUG-40043 is fixed + compare(initialized.scene.primarySubViewport, Qt.rect(0, 0, 50, 50)) + compare(initialized.scene.secondarySubViewport, Qt.rect(50, 50, 100, 100)) + compare(initialized.scene.secondarySubviewOnTop, false) + compare(initialized.scene.selectionQueryPosition, Qt.point(0, 0)) + compare(initialized.scene.slicingActive, true) + compare(initialized.scene.viewport, Qt.rect(0, 0, 100, 100)) + } + } + + TestCase { + name: "Scene3D Change" + + Camera3D { + id: camera1 + zoomLevel: 200 + } + + function test_change() { + change.scene.activeCamera = camera1 + change.scene.devicePixelRatio = 2.0 + change.scene.graphPositionQuery = Qt.point(0, 0) + change.scene.primarySubViewport = Qt.rect(0, 0, 50, 50) + change.scene.secondarySubViewport = Qt.rect(50, 50, 100, 100) + change.scene.secondarySubviewOnTop = false + change.scene.selectionQueryPosition = Qt.point(0, 0) // TODO: When doing signal checks, add tests to check that queries return something (asynchronously) + change.scene.slicingActive = true + + compare(change.scene.activeCamera.zoomLevel, 200) + compare(change.scene.devicePixelRatio, 2.0) + compare(change.scene.graphPositionQuery, Qt.point(0, 0)) + compare(change.scene.primarySubViewport, Qt.rect(0, 0, 50, 50)) + compare(change.scene.secondarySubViewport, Qt.rect(50, 50, 100, 100)) + compare(change.scene.secondarySubviewOnTop, false) + compare(change.scene.selectionQueryPosition, Qt.point(0, 0)) + compare(change.scene.slicingActive, true) + compare(change.scene.viewport, Qt.rect(0, 0, 100, 100)) + } + } } diff --git a/tests/auto/qmltest/surface3d/tst_surfaceseries.qml b/tests/auto/qmltest/surface3d/tst_surfaceseries.qml index 866a04b9..dff2e4a8 100644 --- a/tests/auto/qmltest/surface3d/tst_surfaceseries.qml +++ b/tests/auto/qmltest/surface3d/tst_surfaceseries.qml @@ -67,6 +67,7 @@ Item { drawMode: Surface3DSeries.DrawSurface flatShadingEnabled: false selectedPoint: Qt.point(0, 0) + textureFile: ":\customtexture.jpg" baseColor: "blue" baseGradient: gradient1 @@ -143,6 +144,7 @@ Item { compare(initialized.drawMode, Surface3DSeries.DrawSurface) compare(initialized.flatShadingEnabled, false) compare(initialized.selectedPoint, Qt.point(0, 0)) + compare(initialized.textureFile, ":\customtexture.jpg") } function test_2_initialized_common() { @@ -173,6 +175,7 @@ Item { change.drawMode = Surface3DSeries.DrawSurface change.flatShadingEnabled = false change.selectedPoint = Qt.point(0, 0) + change.textureFile = ":\customtexture.jpg" } function test_2_test_change() { @@ -181,6 +184,7 @@ Item { compare(change.drawMode, Surface3DSeries.DrawSurface) compare(change.flatShadingEnabled, false) compare(change.selectedPoint, Qt.point(0, 0)) + compare(change.textureFile, ":\customtexture.jpg") } function test_3_change_common() { diff --git a/tests/auto/qmltest/theme3d/tst_themecolor.qml b/tests/auto/qmltest/theme3d/tst_themecolor.qml index d7dd3490..421a5775 100644 --- a/tests/auto/qmltest/theme3d/tst_themecolor.qml +++ b/tests/auto/qmltest/theme3d/tst_themecolor.qml @@ -57,7 +57,7 @@ Item { TestCase { name: "ThemeColor Change" - function test_1_change() { + function test_change() { change.color = "blue" compare(change.color, "#0000ff") -- cgit v1.2.3 From 9f6123afc34fd4fb1ea3eb60636b11dc6d911103 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomi=20Korpip=C3=A4=C3=A4?= Date: Wed, 15 Oct 2014 13:02:04 +0300 Subject: Added tests for QML input Also updated missing inheritance to docs Task-number: QTRD-3368 Change-Id: I7b091009d7d89d601215bb4484b8eac7274bcf14 Reviewed-by: Miikka Heikkinen --- .../input/qtouch3dinputhandler.cpp | 1 + tests/auto/qmltest/input3d/tst_input.qml | 83 ++++++++++++++++++++++ tests/auto/qmltest/input3d/tst_touch.qml | 83 ++++++++++++++++++++++ tests/auto/qmltest/qmltest.pro | 8 +-- 4 files changed, 171 insertions(+), 4 deletions(-) create mode 100644 tests/auto/qmltest/input3d/tst_input.qml create mode 100644 tests/auto/qmltest/input3d/tst_touch.qml diff --git a/src/datavisualization/input/qtouch3dinputhandler.cpp b/src/datavisualization/input/qtouch3dinputhandler.cpp index 20eebb58..357a6f3e 100644 --- a/src/datavisualization/input/qtouch3dinputhandler.cpp +++ b/src/datavisualization/input/qtouch3dinputhandler.cpp @@ -76,6 +76,7 @@ static const float touchZoomDrift = 0.02f; * \since QtDataVisualization 1.2 * \ingroup datavisualization_qml * \instantiates QTouch3DInputHandler + * \inherits InputHandler3D * \brief Basic touch display based input handler. * * TouchInputHandler3D is the basic input handler for touch screen devices. diff --git a/tests/auto/qmltest/input3d/tst_input.qml b/tests/auto/qmltest/input3d/tst_input.qml new file mode 100644 index 00000000..fefb585e --- /dev/null +++ b/tests/auto/qmltest/input3d/tst_input.qml @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +import QtQuick 2.0 +import QtDataVisualization 1.2 +import QtTest 1.0 + +Item { + id: top + width: 150 + height: 150 + + InputHandler3D { + id: initial + } + + InputHandler3D { + id: initialized + rotationEnabled: false + selectionEnabled: false + zoomAtTargetEnabled: false + zoomEnabled: false + } + + InputHandler3D { + id: change + } + + TestCase { + name: "InputHandler3D Initial" + + function test_initial() { + compare(initial.rotationEnabled, true) + compare(initial.selectionEnabled, true) + compare(initial.zoomAtTargetEnabled, true) + compare(initial.zoomEnabled, true) + } + } + + TestCase { + name: "InputHandler3D Initialized" + + function test_initialized() { + compare(initialized.rotationEnabled, false) + compare(initialized.selectionEnabled, false) + compare(initialized.zoomAtTargetEnabled, false) + compare(initialized.zoomEnabled, false) + } + } + + TestCase { + name: "InputHandler3D Change" + + function test_change() { + change.rotationEnabled = false + change.selectionEnabled = false + change.zoomAtTargetEnabled = false + change.zoomEnabled = false + + compare(change.rotationEnabled, false) + compare(change.selectionEnabled, false) + compare(change.zoomAtTargetEnabled, false) + compare(change.zoomEnabled, false) + } + + // TODO: QTRD-3380 (mouse events) + } +} diff --git a/tests/auto/qmltest/input3d/tst_touch.qml b/tests/auto/qmltest/input3d/tst_touch.qml new file mode 100644 index 00000000..626da68f --- /dev/null +++ b/tests/auto/qmltest/input3d/tst_touch.qml @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +import QtQuick 2.0 +import QtDataVisualization 1.2 +import QtTest 1.0 + +Item { + id: top + width: 150 + height: 150 + + TouchInputHandler3D { + id: initial + } + + TouchInputHandler3D { + id: initialized + rotationEnabled: false + selectionEnabled: false + zoomAtTargetEnabled: false + zoomEnabled: false + } + + TouchInputHandler3D { + id: change + } + + TestCase { + name: "TouchInputHandler3D Initial" + + function test_initial() { + compare(initial.rotationEnabled, true) + compare(initial.selectionEnabled, true) + compare(initial.zoomAtTargetEnabled, true) + compare(initial.zoomEnabled, true) + } + } + + TestCase { + name: "TouchInputHandler3D Initialized" + + function test_initialized() { + compare(initialized.rotationEnabled, false) + compare(initialized.selectionEnabled, false) + compare(initialized.zoomAtTargetEnabled, false) + compare(initialized.zoomEnabled, false) + } + } + + TestCase { + name: "TouchInputHandler3D Change" + + function test_change() { + change.rotationEnabled = false + change.selectionEnabled = false + change.zoomAtTargetEnabled = false + change.zoomEnabled = false + + compare(change.rotationEnabled, false) + compare(change.selectionEnabled, false) + compare(change.zoomAtTargetEnabled, false) + compare(change.zoomEnabled, false) + + // TODO: QTRD-3380 (mouse events) + } + } +} diff --git a/tests/auto/qmltest/qmltest.pro b/tests/auto/qmltest/qmltest.pro index f660f759..ac52caaf 100644 --- a/tests/auto/qmltest/qmltest.pro +++ b/tests/auto/qmltest/qmltest.pro @@ -23,12 +23,12 @@ OTHER_FILES += bars3d\tst_basic.qml \ custom3d\tst_customvolume.qml \ scene3d\tst_scene.qml \ scene3d\tst_camera.qml \ - scene3d\tst_light.qml #\ + scene3d\tst_light.qml \ + input3d\tst_input.qml \ + input3d\tst_touch.qml #\ # axis3d\tst_category.qml \ # axis3d\tst_value.qml \ -# axis3d\tst_logvalue.qml - -# TODO: Check new QML types in 1.2 +# axis3d\tst_logvalue.qml \ RESOURCES += \ qmltest.qrc -- cgit v1.2.3 From bafaf813356e308311bd1174b71e3c16c9fa0049 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Wed, 15 Oct 2014 12:59:27 +0300 Subject: Fix scatter item autosizing. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Autosizing only accounted for freshly changed series when calculating the total item count visible in the graph. Change-Id: I7c0990123d3e3827c3518713f5389fedf91aca92 Reviewed-by: Tomi Korpipää --- src/datavisualization/engine/scatter3drenderer.cpp | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/datavisualization/engine/scatter3drenderer.cpp b/src/datavisualization/engine/scatter3drenderer.cpp index 8ccd0824..d2f085be 100644 --- a/src/datavisualization/engine/scatter3drenderer.cpp +++ b/src/datavisualization/engine/scatter3drenderer.cpp @@ -184,23 +184,25 @@ void Scatter3DRenderer::updateData() foreach (SeriesRenderCache *baseCache, m_renderCacheList) { ScatterSeriesRenderCache *cache = static_cast(baseCache); - if (cache->isVisible() && cache->dataDirty()) { + if (cache->isVisible()) { const QScatter3DSeries *currentSeries = cache->series(); ScatterRenderItemArray &renderArray = cache->renderArray(); QScatterDataProxy *dataProxy = currentSeries->dataProxy(); const QScatterDataArray &dataArray = *dataProxy->array(); int dataSize = dataArray.size(); totalDataSize += dataSize; - if (dataSize != renderArray.size()) - renderArray.resize(dataSize); + if (cache->dataDirty()) { + if (dataSize != renderArray.size()) + renderArray.resize(dataSize); - for (int i = 0; i < dataSize; i++) - updateRenderItem(dataArray.at(i), renderArray[i]); + for (int i = 0; i < dataSize; i++) + updateRenderItem(dataArray.at(i), renderArray[i]); - if (m_cachedOptimizationHint.testFlag(QAbstract3DGraph::OptimizationStatic)) - cache->setStaticBufferDirty(true); + if (m_cachedOptimizationHint.testFlag(QAbstract3DGraph::OptimizationStatic)) + cache->setStaticBufferDirty(true); - cache->setDataDirty(false); + cache->setDataDirty(false); + } } } -- cgit v1.2.3 From e12a902adc6443a55c6207410599af7ed966f122 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomi=20Korpip=C3=A4=C3=A4?= Date: Thu, 16 Oct 2014 07:48:49 +0300 Subject: Added tests for QML axes Task-number: QTRD-3368 Change-Id: Ie7be01bdbd0f63eccde39af86d1423d0af5df1b3 Change-Id: Ie7be01bdbd0f63eccde39af86d1423d0af5df1b3 Reviewed-by: Miikka Heikkinen --- tests/auto/qmltest/axis3d/tst_category.qml | 89 ++++++++++++++++++++++++- tests/auto/qmltest/axis3d/tst_logvalue.qml | 49 +++++++++++++- tests/auto/qmltest/axis3d/tst_value.qml | 103 ++++++++++++++++++++++++++++- tests/auto/qmltest/bars3d/tst_basic.qml | 6 ++ tests/auto/qmltest/qmltest.pro | 8 +-- tests/auto/qmltest/scatter3d/tst_basic.qml | 6 ++ tests/auto/qmltest/surface3d/tst_basic.qml | 6 ++ 7 files changed, 260 insertions(+), 7 deletions(-) diff --git a/tests/auto/qmltest/axis3d/tst_category.qml b/tests/auto/qmltest/axis3d/tst_category.qml index 98e1779f..7f0be845 100644 --- a/tests/auto/qmltest/axis3d/tst_category.qml +++ b/tests/auto/qmltest/axis3d/tst_category.qml @@ -25,5 +25,92 @@ Item { height: 150 width: 150 - // TODO: Add tests for CategoryAxis3D + CategoryAxis3D { + id: initial + } + + CategoryAxis3D { + id: initialized + labels: ["first", "second"] + + autoAdjustRange: false + labelAutoRotation: 10.0 + max: 20 + min: 10 + title: "initialized" + titleFixed: false + titleVisible: true + } + + CategoryAxis3D { + id: change + } + + TestCase { + name: "CategoryAxis3D Initial" + + function test_initial() { + compare(initial.labels.length, 0) + + compare(initial.autoAdjustRange, true) + compare(initial.labelAutoRotation, 0.0) + compare(initial.max, 10) + compare(initial.min, 0) + compare(initial.orientation, AbstractAxis3D.AxisOrientationNone) + compare(initial.title, "") + compare(initial.titleFixed, true) + compare(initial.titleVisible, false) + compare(initial.type, AbstractAxis3D.AxisTypeCategory) + } + } + + TestCase { + name: "CategoryAxis3D Initialized" + + function test_initialized() { + compare(initialized.labels.length, 2) + compare(initialized.labels[0], "first") + compare(initialized.labels[1], "second") + + compare(initialized.autoAdjustRange, false) + compare(initialized.labelAutoRotation, 10.0) + compare(initialized.max, 20) + compare(initialized.min, 10) + compare(initialized.title, "initialized") + compare(initialized.titleFixed, false) + compare(initialized.titleVisible, true) + } + } + + TestCase { + name: "CategoryAxis3D Change" + + function test_change() { + change.labels = ["first"] + compare(change.labels.length, 1) + compare(change.labels[0], "first") + change.labels = ["first", "second"] + compare(change.labels.length, 2) + compare(change.labels[0], "first") + compare(change.labels[1], "second") + change.labels[1] = "another" + compare(change.labels[1], "another") + + change.autoAdjustRange = false + change.labelAutoRotation = 10.0 + change.max = 20 + change.min = 10 + change.title = "initialized" + change.titleFixed = false + change.titleVisible = true + + compare(change.autoAdjustRange, false) + compare(change.labelAutoRotation, 10.0) + compare(change.max, 20) + compare(change.min, 10) + compare(change.title, "initialized") + compare(change.titleFixed, false) + compare(change.titleVisible, true) + } + } } diff --git a/tests/auto/qmltest/axis3d/tst_logvalue.qml b/tests/auto/qmltest/axis3d/tst_logvalue.qml index 7c6dc83c..fad93611 100644 --- a/tests/auto/qmltest/axis3d/tst_logvalue.qml +++ b/tests/auto/qmltest/axis3d/tst_logvalue.qml @@ -25,5 +25,52 @@ Item { height: 150 width: 150 - // TODO: Add tests for LogValueAxis3DFormatter + LogValueAxis3DFormatter { + id: initial + } + + LogValueAxis3DFormatter { + id: initialized + autoSubGrid: false + base: 0.1 + showEdgeLabels: false + } + + LogValueAxis3DFormatter { + id: change + } + + TestCase { + name: "LogValueAxis3DFormatter Initial" + + function test_initial() { + compare(initial.autoSubGrid, true) + compare(initial.base, 10) + compare(initial.showEdgeLabels, true) + } + } + + TestCase { + name: "LogValueAxis3DFormatter Initialized" + + function test_initialized() { + compare(initialized.autoSubGrid, false) + compare(initialized.base, 0.1) + compare(initialized.showEdgeLabels, false) + } + } + + TestCase { + name: "LogValueAxis3DFormatter Change" + + function test_change() { + change.autoSubGrid = false + change.base = 0.1 + change.showEdgeLabels = false + + compare(change.autoSubGrid, false) + compare(change.base, 0.1) + compare(change.showEdgeLabels, false) + } + } } diff --git a/tests/auto/qmltest/axis3d/tst_value.qml b/tests/auto/qmltest/axis3d/tst_value.qml index fa70608b..6a8f7849 100644 --- a/tests/auto/qmltest/axis3d/tst_value.qml +++ b/tests/auto/qmltest/axis3d/tst_value.qml @@ -25,5 +25,106 @@ Item { height: 150 width: 150 - // TODO: Add tests for ValueAxis3D + ValueAxis3D { + id: initial + } + + ValueAxis3D { + id: initialized + formatter: ValueAxis3DFormatter { objectName: "formatter1" } + labelFormat: "%f" + reversed: true + segmentCount: 10 + subSegmentCount: 5 + + autoAdjustRange: false + labelAutoRotation: 10.0 + max: 20 + min: -10 + title: "initialized" + titleFixed: false + titleVisible: true + } + + ValueAxis3D { + id: change + } + + TestCase { + name: "ValueAxis3D Initial" + + function test_initial() { + verify(initial.formatter) + compare(initial.labelFormat, "%.2f") + compare(initial.reversed, false) + compare(initial.segmentCount, 5) + compare(initial.subSegmentCount, 1) + + compare(initial.autoAdjustRange, true) + compare(initial.labelAutoRotation, 0.0) + compare(initial.max, 10) + compare(initial.min, 0) + compare(initial.orientation, AbstractAxis3D.AxisOrientationNone) + compare(initial.title, "") + compare(initial.titleFixed, true) + compare(initial.titleVisible, false) + compare(initial.type, AbstractAxis3D.AxisTypeValue) + } + } + + TestCase { + name: "ValueAxis3D Initialized" + + function test_initialized() { + compare(initialized.formatter.objectName, "formatter1") + compare(initialized.labelFormat, "%f") + compare(initialized.reversed, true) + compare(initialized.segmentCount, 10) + compare(initialized.subSegmentCount, 5) + + compare(initialized.autoAdjustRange, false) + compare(initialized.labelAutoRotation, 10.0) + compare(initialized.max, 20) + compare(initialized.min, -10) + compare(initialized.title, "initialized") + compare(initialized.titleFixed, false) + compare(initialized.titleVisible, true) + } + } + + TestCase { + name: "ValueAxis3D Change" + + ValueAxis3DFormatter { id: formatter1 } + + function test_change() { + change.formatter = formatter1 + change.labelFormat = "%f" + change.reversed = true + change.segmentCount = 10 + change.subSegmentCount = 5 + + compare(change.formatter, formatter1) + compare(change.labelFormat, "%f") + compare(change.reversed, true) + compare(change.segmentCount, 10) + compare(change.subSegmentCount, 5) + + change.autoAdjustRange = false + change.labelAutoRotation = 10.0 + change.max = 20 + change.min = -10 + change.title = "initialized" + change.titleFixed = false + change.titleVisible = true + + compare(change.autoAdjustRange, false) + compare(change.labelAutoRotation, 10.0) + compare(change.max, 20) + compare(change.min, -10) + compare(change.title, "initialized") + compare(change.titleFixed, false) + compare(change.titleVisible, true) + } + } } diff --git a/tests/auto/qmltest/bars3d/tst_basic.qml b/tests/auto/qmltest/bars3d/tst_basic.qml index 6c908623..cfba9b4a 100644 --- a/tests/auto/qmltest/bars3d/tst_basic.qml +++ b/tests/auto/qmltest/bars3d/tst_basic.qml @@ -80,6 +80,12 @@ Item { compare(empty.selectedSeries, null, "selectedSeries") compare(empty.primarySeries, null, "primarySeries") compare(empty.floorLevel, 0.0, "floorLevel") + compare(empty.columnAxis.orientation, AbstractAxis3D.AxisOrientationX) + compare(empty.rowAxis.orientation, AbstractAxis3D.AxisOrientationZ) + compare(empty.valueAxis.orientation, AbstractAxis3D.AxisOrientationY) + compare(empty.columnAxis.type, AbstractAxis3D.AxisTypeCategory) + compare(empty.rowAxis.type, AbstractAxis3D.AxisTypeCategory) + compare(empty.valueAxis.type, AbstractAxis3D.AxisTypeValue) } } diff --git a/tests/auto/qmltest/qmltest.pro b/tests/auto/qmltest/qmltest.pro index ac52caaf..d70d7c88 100644 --- a/tests/auto/qmltest/qmltest.pro +++ b/tests/auto/qmltest/qmltest.pro @@ -25,10 +25,10 @@ OTHER_FILES += bars3d\tst_basic.qml \ scene3d\tst_camera.qml \ scene3d\tst_light.qml \ input3d\tst_input.qml \ - input3d\tst_touch.qml #\ -# axis3d\tst_category.qml \ -# axis3d\tst_value.qml \ -# axis3d\tst_logvalue.qml \ + input3d\tst_touch.qml \ + axis3d\tst_category.qml \ + axis3d\tst_value.qml \ + axis3d\tst_logvalue.qml \ RESOURCES += \ qmltest.qrc diff --git a/tests/auto/qmltest/scatter3d/tst_basic.qml b/tests/auto/qmltest/scatter3d/tst_basic.qml index b9d9c93c..985236be 100644 --- a/tests/auto/qmltest/scatter3d/tst_basic.qml +++ b/tests/auto/qmltest/scatter3d/tst_basic.qml @@ -68,6 +68,12 @@ Item { compare(empty.height, 0, "height") compare(empty.seriesList.length, 0, "seriesList") compare(empty.selectedSeries, null, "selectedSeries") + compare(empty.axisX.orientation, AbstractAxis3D.AxisOrientationX) + compare(empty.axisZ.orientation, AbstractAxis3D.AxisOrientationZ) + compare(empty.axisY.orientation, AbstractAxis3D.AxisOrientationY) + compare(empty.axisX.type, AbstractAxis3D.AxisTypeValue) + compare(empty.axisZ.type, AbstractAxis3D.AxisTypeValue) + compare(empty.axisY.type, AbstractAxis3D.AxisTypeValue) } } diff --git a/tests/auto/qmltest/surface3d/tst_basic.qml b/tests/auto/qmltest/surface3d/tst_basic.qml index 17d3d614..ba82a524 100644 --- a/tests/auto/qmltest/surface3d/tst_basic.qml +++ b/tests/auto/qmltest/surface3d/tst_basic.qml @@ -70,6 +70,12 @@ Item { compare(empty.seriesList.length, 0, "seriesList") compare(empty.selectedSeries, null, "selectedSeries") compare(empty.flipHorizontalGrid, false, "flipHorizontalGrid") + compare(empty.axisX.orientation, AbstractAxis3D.AxisOrientationX) + compare(empty.axisZ.orientation, AbstractAxis3D.AxisOrientationZ) + compare(empty.axisY.orientation, AbstractAxis3D.AxisOrientationY) + compare(empty.axisX.type, AbstractAxis3D.AxisTypeValue) + compare(empty.axisZ.type, AbstractAxis3D.AxisTypeValue) + compare(empty.axisY.type, AbstractAxis3D.AxisTypeValue) } } -- cgit v1.2.3 From e69acebea5eae430dbf988f379cd26464930ee6b Mon Sep 17 00:00:00 2001 From: Mika Salmela Date: Thu, 16 Oct 2014 10:04:32 +0300 Subject: Regenerate render buffer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Looks like the render buffer for depth gets incomplete and that causes resets. Regenerating the buffers seems to be helpful. Task-number: QTRD-3372 Change-Id: I1f0fb40f06fac44e062f2f2b512bc9424479328c Reviewed-by: Tomi Korpipää Reviewed-by: Miikka Heikkinen --- src/datavisualization/utils/texturehelper.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/datavisualization/utils/texturehelper.cpp b/src/datavisualization/utils/texturehelper.cpp index 1d8de71d..2c40eb6a 100644 --- a/src/datavisualization/utils/texturehelper.cpp +++ b/src/datavisualization/utils/texturehelper.cpp @@ -176,8 +176,10 @@ GLuint TextureHelper::createSelectionTexture(const QSize &size, GLuint &frameBuf glBindTexture(GL_TEXTURE_2D, 0); // Create render buffer - if (!depthBuffer) - glGenRenderbuffers(1, &depthBuffer); + if (depthBuffer) + glDeleteRenderbuffers(1, &depthBuffer); + + glGenRenderbuffers(1, &depthBuffer); glBindRenderbuffer(GL_RENDERBUFFER, depthBuffer); GLenum status = glGetError(); // glGetError docs advise to call glGetError in loop to clear all error flags -- cgit v1.2.3 From a24d3628fceb70eb2cba2fa91883f5f3bfa55361 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomi=20Korpip=C3=A4=C3=A4?= Date: Thu, 16 Oct 2014 13:22:47 +0300 Subject: Test for QML proxies Also added missing default values to some docs. Task-number: QTRD-3368 Change-Id: I98940a80d6edfe60801c6b2606307b3ec3ab8c39 Reviewed-by: Miikka Heikkinen --- .../data/qheightmapsurfacedataproxy.cpp | 16 +- tests/auto/qmltest/bars3d/tst_proxy.qml | 226 +++++++++++++++++++- tests/auto/qmltest/qmltest.pro | 7 +- tests/auto/qmltest/scatter3d/tst_proxy.qml | 107 +++++++++- tests/auto/qmltest/surface3d/tst_heightproxy.qml | 101 +++++++++ tests/auto/qmltest/surface3d/tst_proxy.qml | 236 ++++++++++++++++++++- 6 files changed, 679 insertions(+), 14 deletions(-) create mode 100644 tests/auto/qmltest/surface3d/tst_heightproxy.qml diff --git a/src/datavisualization/data/qheightmapsurfacedataproxy.cpp b/src/datavisualization/data/qheightmapsurfacedataproxy.cpp index d64046be..85aed432 100644 --- a/src/datavisualization/data/qheightmapsurfacedataproxy.cpp +++ b/src/datavisualization/data/qheightmapsurfacedataproxy.cpp @@ -84,7 +84,7 @@ const float defaultMaxValue = 10.0f; /*! * \qmlproperty real HeightMapSurfaceDataProxy::minXValue * - * The minimum X value for the generated surface points. + * The minimum X value for the generated surface points. Defaults to \c{0.0}. * When setting this property the corresponding maximum value is adjusted if necessary, * to ensure that the range remains valid. */ @@ -92,7 +92,7 @@ const float defaultMaxValue = 10.0f; /*! * \qmlproperty real HeightMapSurfaceDataProxy::maxXValue * - * The maximum X value for the generated surface points. + * The maximum X value for the generated surface points. Defaults to \c{10.0}. * When setting this property the corresponding minimum value is adjusted if necessary, * to ensure that the range remains valid. */ @@ -100,7 +100,7 @@ const float defaultMaxValue = 10.0f; /*! * \qmlproperty real HeightMapSurfaceDataProxy::minZValue * - * The minimum Z value for the generated surface points. + * The minimum Z value for the generated surface points. Defaults to \c{0.0}. * When setting this property the corresponding maximum value is adjusted if necessary, * to ensure that the range remains valid. */ @@ -108,7 +108,7 @@ const float defaultMaxValue = 10.0f; /*! * \qmlproperty real HeightMapSurfaceDataProxy::maxZValue * - * The maximum Z value for the generated surface points. + * The maximum Z value for the generated surface points. Defaults to \c{10.0}. * When setting this property the corresponding minimum value is adjusted if necessary, * to ensure that the range remains valid. */ @@ -228,7 +228,7 @@ void QHeightMapSurfaceDataProxy::setValueRanges(float minX, float maxX, float mi /*! * \property QHeightMapSurfaceDataProxy::minXValue * - * The minimum X value for the generated surface points. + * The minimum X value for the generated surface points. Defaults to \c{0.0}. * When setting this property the corresponding maximum value is adjusted if necessary, * to ensure that the range remains valid. */ @@ -245,7 +245,7 @@ float QHeightMapSurfaceDataProxy::minXValue() const /*! * \property QHeightMapSurfaceDataProxy::maxXValue * - * The maximum X value for the generated surface points. + * The maximum X value for the generated surface points. Defaults to \c{10.0}. * When setting this property the corresponding minimum value is adjusted if necessary, * to ensure that the range remains valid. */ @@ -262,7 +262,7 @@ float QHeightMapSurfaceDataProxy::maxXValue() const /*! * \property QHeightMapSurfaceDataProxy::minZValue * - * The minimum Z value for the generated surface points. + * The minimum Z value for the generated surface points. Defaults to \c{0.0}. * When setting this property the corresponding maximum value is adjusted if necessary, * to ensure that the range remains valid. */ @@ -279,7 +279,7 @@ float QHeightMapSurfaceDataProxy::minZValue() const /*! * \property QHeightMapSurfaceDataProxy::maxZValue * - * The maximum Z value for the generated surface points. + * The maximum Z value for the generated surface points. Defaults to \c{10.0}. * When setting this property the corresponding minimum value is adjusted if necessary, * to ensure that the range remains valid. */ diff --git a/tests/auto/qmltest/bars3d/tst_proxy.qml b/tests/auto/qmltest/bars3d/tst_proxy.qml index 7c117ce4..8d91c055 100644 --- a/tests/auto/qmltest/bars3d/tst_proxy.qml +++ b/tests/auto/qmltest/bars3d/tst_proxy.qml @@ -25,5 +25,229 @@ Item { height: 150 width: 150 - // TODO: Add tests for ItemModelBarDataProxy + ItemModelBarDataProxy { + id: initial + } + + ItemModelBarDataProxy { + id: initialized + + autoColumnCategories: false + autoRowCategories: false + columnCategories: ["colcat1", "colcat2"] + columnRole: "col" + columnRolePattern: /^.*-(\d\d)$/ + columnRoleReplace: "\\1" + itemModel: ListModel { objectName: "model1" } + multiMatchBehavior: ItemModelBarDataProxy.MMBAverage + rotationRole: "rot" + rotationRolePattern: /-/ + rotationRoleReplace: "\\1" + rowCategories: ["rowcat1", "rowcat2"] + rowRole: "row" + rowRolePattern: /^(\d\d\d\d).*$/ + rowRoleReplace: "\\1" + valueRole: "val" + valueRolePattern: /-/ + valueRoleReplace: "\\1" + + columnLabels: ["col1", "col2"] + rowLabels: ["row1", "row2"] + } + + ItemModelBarDataProxy { + id: change + } + + TestCase { + name: "ItemModelBarDataProxy Initial" + + function test_initial() { + compare(initial.autoColumnCategories, true) + compare(initial.autoRowCategories, true) + compare(initial.columnCategories, []) + compare(initial.columnRole, "") + verify(initial.columnRolePattern) + compare(initial.columnRoleReplace, "") + verify(!initial.itemModel) + compare(initial.multiMatchBehavior, ItemModelBarDataProxy.MMBLast) + compare(initial.rotationRole, "") + verify(initial.rotationRolePattern) + compare(initial.rotationRoleReplace, "") + compare(initial.rowCategories, []) + compare(initial.rowRole, "") + verify(initial.rowRolePattern) + compare(initial.rowRoleReplace, "") + compare(initial.useModelCategories, false) + compare(initial.valueRole, "") + verify(initial.valueRolePattern) + compare(initial.valueRoleReplace, "") + + compare(initial.columnLabels.length, 0) + compare(initial.rowCount, 0) + compare(initial.rowLabels.length, 0) + verify(!initial.series) + + compare(initial.type, AbstractDataProxy.DataTypeBar) + } + } + + TestCase { + name: "ItemModelBarDataProxy Initialized" + + function test_initialized() { + compare(initialized.autoColumnCategories, false) + compare(initialized.autoRowCategories, false) + compare(initialized.columnCategories.length, 2) + compare(initialized.columnCategories[0], "colcat1") + compare(initialized.columnCategories[1], "colcat2") + compare(initialized.columnRole, "col") + compare(initialized.columnRolePattern, /^.*-(\d\d)$/) + compare(initialized.columnRoleReplace, "\\1") + compare(initialized.itemModel.objectName, "model1") + compare(initialized.multiMatchBehavior, ItemModelBarDataProxy.MMBAverage) + compare(initialized.rotationRole, "rot") + compare(initialized.rotationRolePattern, /-/) + compare(initialized.rotationRoleReplace, "\\1") + compare(initialized.rowCategories.length, 2) + compare(initialized.rowCategories[0], "rowcat1") + compare(initialized.rowCategories[1], "rowcat2") + compare(initialized.rowRole, "row") + compare(initialized.rowRolePattern, /^(\d\d\d\d).*$/) + compare(initialized.rowRoleReplace, "\\1") + compare(initialized.valueRole, "val") + compare(initialized.valueRolePattern, /-/) + compare(initialized.valueRoleReplace, "\\1") + + compare(initialized.columnLabels.length, 2) + compare(initialized.rowCount, 2) + compare(initialized.rowLabels.length, 2) + } + } + + TestCase { + name: "ItemModelBarDataProxy Change" + + ListModel { id: model1; objectName: "model1" } + + function test_1_change() { + change.autoColumnCategories = false + change.autoRowCategories = false + change.columnCategories = ["colcat1", "colcat2"] + change.columnRole = "col" + change.columnRolePattern = /^.*-(\d\d)$/ + change.columnRoleReplace = "\\1" + change.itemModel = model1 + change.multiMatchBehavior = ItemModelBarDataProxy.MMBAverage + change.rotationRole = "rot" + change.rotationRolePattern = /-/ + change.rotationRoleReplace = "\\1" + change.rowCategories = ["rowcat1", "rowcat2"] + change.rowRole = "row" + change.rowRolePattern = /^(\d\d\d\d).*$/ + change.rowRoleReplace = "\\1" + change.useModelCategories = true // Overwrites columnLabels and rowLabels + change.valueRole = "val" + change.valueRolePattern = /-/ + change.valueRoleReplace = "\\1" + + change.columnLabels = ["col1", "col2"] + change.rowLabels = ["row1", "row2"] + } + + function test_2_test_change() { + // This test has a dependency to the previous one due to asynchronous item model resolving + compare(change.autoColumnCategories, false) + compare(change.autoRowCategories, false) + compare(change.columnCategories.length, 2) + compare(change.columnCategories[0], "colcat1") + compare(change.columnCategories[1], "colcat2") + compare(change.columnRole, "col") + compare(change.columnRolePattern, /^.*-(\d\d)$/) + compare(change.columnRoleReplace, "\\1") + compare(change.itemModel.objectName, "model1") + compare(change.multiMatchBehavior, ItemModelBarDataProxy.MMBAverage) + compare(change.rotationRole, "rot") + compare(change.rotationRolePattern, /-/) + compare(change.rotationRoleReplace, "\\1") + compare(change.rowCategories.length, 2) + compare(change.rowCategories[0], "rowcat1") + compare(change.rowCategories[1], "rowcat2") + compare(change.rowRole, "row") + compare(change.rowRolePattern, /^(\d\d\d\d).*$/) + compare(change.rowRoleReplace, "\\1") + compare(change.useModelCategories, true) + compare(change.valueRole, "val") + compare(change.valueRolePattern, /-/) + compare(change.valueRoleReplace, "\\1") + + compare(change.columnLabels.length, 1) + compare(change.rowCount, 0) + compare(change.rowLabels.length, 0) + } + } + + TestCase { + name: "ItemModelBarDataProxy MultiMatchBehaviour" + + Bars3D { + id: bars1 + + Bar3DSeries { + ItemModelBarDataProxy { + id: barProxy + itemModel: ListModel { + ListElement{ coords: "0,0"; data: "5"; } + ListElement{ coords: "0,0"; data: "15"; } + } + rowRole: "coords" + columnRole: "coords" + valueRole: "data" + rowRolePattern: /(\d),\d/ + columnRolePattern: /(\d),(\d)/ + rowRoleReplace: "\\1" + columnRoleReplace: "\\2" + } + } + } + + function test_0_async_dummy() { + } + + function test_1_test_multimatch() { + compare(bars1.valueAxis.max, 15) + } + + function test_2_multimatch() { + barProxy.multiMatchBehavior = ItemModelBarDataProxy.MMBFirst + } + + function test_3_test_multimatch() { + compare(bars1.valueAxis.max, 5) + } + + function test_4_multimatch() { + barProxy.multiMatchBehavior = ItemModelBarDataProxy.MMBLast + } + + function test_5_test_multimatch() { + compare(bars1.valueAxis.max, 15) + } + + function test_6_multimatch() { + barProxy.multiMatchBehavior = ItemModelBarDataProxy.MMBAverage + } + + function test_7_test_multimatch() { + compare(bars1.valueAxis.max, 10) + } + + function test_8_multimatch() { + barProxy.multiMatchBehavior = ItemModelBarDataProxy.MMBCumulative + } + + function test_9_test_multimatch() { + compare(bars1.valueAxis.max, 20) + } + } } diff --git a/tests/auto/qmltest/qmltest.pro b/tests/auto/qmltest/qmltest.pro index d70d7c88..cbb9b8b8 100644 --- a/tests/auto/qmltest/qmltest.pro +++ b/tests/auto/qmltest/qmltest.pro @@ -6,15 +6,16 @@ SOURCES += tst_qmltest.cpp OTHER_FILES += bars3d\tst_basic.qml \ bars3d\tst_bars.qml \ bars3d\tst_barseries.qml \ - #bars3d\tst_proxy.qml \ + bars3d\tst_proxy.qml \ scatter3d\tst_basic.qml \ scatter3d\tst_scatter.qml \ scatter3d\tst_scatterseries.qml \ - #scatter3d\tst_proxy.qml \ + scatter3d\tst_proxy.qml \ surface3d\tst_basic.qml \ surface3d\tst_surface.qml \ surface3d\tst_surfaceseries.qml \ - #surface3d\tst_proxy.qml \ + surface3d\tst_proxy.qml \ + surface3d\tst_heightproxy.qml \ theme3d\tst_theme.qml \ theme3d\tst_colorgradient.qml \ theme3d\tst_themecolor.qml \ diff --git a/tests/auto/qmltest/scatter3d/tst_proxy.qml b/tests/auto/qmltest/scatter3d/tst_proxy.qml index 4476bf68..e6478f15 100644 --- a/tests/auto/qmltest/scatter3d/tst_proxy.qml +++ b/tests/auto/qmltest/scatter3d/tst_proxy.qml @@ -25,5 +25,110 @@ Item { height: 150 width: 150 - // TODO: Add tests for ItemModelScatterDataProxy + ItemModelScatterDataProxy { + id: initial + } + + ItemModelScatterDataProxy { + id: initialized + + itemModel: ListModel { objectName: "model1" } + rotationRole: "rot" + rotationRolePattern: /-/ + rotationRoleReplace: "\\1" + xPosRole: "x" + xPosRolePattern: /^.*-(\d\d)$/ + xPosRoleReplace: "\\1" + yPosRole: "y" + yPosRolePattern: /^(\d\d\d\d).*$/ + yPosRoleReplace: "\\1" + zPosRole: "z" + zPosRolePattern: /-/ + zPosRoleReplace: "\\1" + } + + ItemModelScatterDataProxy { + id: change + } + + TestCase { + name: "ItemModelScatterDataProxy Initial" + + function test_initial() { + verify(!initial.itemModel) + compare(initial.rotationRole, "") + verify(initial.rotationRolePattern) + compare(initial.rotationRoleReplace, "") + compare(initial.xPosRole, "") + verify(initial.xPosRolePattern) + compare(initial.xPosRoleReplace, "") + compare(initial.yPosRole, "") + verify(initial.yPosRolePattern) + compare(initial.yPosRoleReplace, "") + compare(initial.zPosRole, "") + verify(initial.zPosRolePattern) + compare(initial.zPosRoleReplace, "") + + compare(initial.itemCount, 0) + verify(!initial.series) + + compare(initial.type, AbstractDataProxy.DataTypeScatter) + } + } + + TestCase { + name: "ItemModelScatterDataProxy Initialized" + + function test_initialized() { + compare(initialized.itemModel.objectName, "model1") + compare(initialized.rotationRole, "rot") + compare(initialized.rotationRolePattern, /-/) + compare(initialized.rotationRoleReplace, "\\1") + compare(initialized.xPosRole, "x") + compare(initialized.xPosRolePattern, /^.*-(\d\d)$/) + compare(initialized.xPosRoleReplace, "\\1") + compare(initialized.yPosRole, "y") + compare(initialized.yPosRolePattern, /^(\d\d\d\d).*$/) + compare(initialized.yPosRoleReplace, "\\1") + compare(initialized.zPosRole, "z") + compare(initialized.zPosRolePattern, /-/) + compare(initialized.zPosRoleReplace, "\\1") + } + } + + TestCase { + name: "ItemModelScatterDataProxy Change" + + ListModel { id: model1; objectName: "model1" } + + function test_change() { + change.itemModel = model1 + change.rotationRole = "rot" + change.rotationRolePattern = /-/ + change.rotationRoleReplace = "\\1" + change.xPosRole = "x" + change.xPosRolePattern = /^.*-(\d\d)$/ + change.xPosRoleReplace = "\\1" + change.yPosRole = "y" + change.yPosRolePattern = /^(\d\d\d\d).*$/ + change.yPosRoleReplace = "\\1" + change.zPosRole = "z" + change.zPosRolePattern = /-/ + change.zPosRoleReplace = "\\1" + + compare(change.itemModel.objectName, "model1") + compare(change.rotationRole, "rot") + compare(change.rotationRolePattern, /-/) + compare(change.rotationRoleReplace, "\\1") + compare(change.xPosRole, "x") + compare(change.xPosRolePattern, /^.*-(\d\d)$/) + compare(change.xPosRoleReplace, "\\1") + compare(change.yPosRole, "y") + compare(change.yPosRolePattern, /^(\d\d\d\d).*$/) + compare(change.yPosRoleReplace, "\\1") + compare(change.zPosRole, "z") + compare(change.zPosRolePattern, /-/) + compare(change.zPosRoleReplace, "\\1") + } + } } diff --git a/tests/auto/qmltest/surface3d/tst_heightproxy.qml b/tests/auto/qmltest/surface3d/tst_heightproxy.qml new file mode 100644 index 00000000..dc6938f8 --- /dev/null +++ b/tests/auto/qmltest/surface3d/tst_heightproxy.qml @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +import QtQuick 2.0 +import QtDataVisualization 1.2 +import QtTest 1.0 + +Item { + id: top + height: 150 + width: 150 + + HeightMapSurfaceDataProxy { + id: initial + } + + HeightMapSurfaceDataProxy { + id: initialized + heightMapFile: ":/customtexture.jpg" + maxXValue: 10.0 + maxZValue: 10.0 + minXValue: -10.0 + minZValue: -10.0 + } + + HeightMapSurfaceDataProxy { + id: change + } + + TestCase { + name: "HeightMapSurfaceDataProxy Initial" + + function test_initial() { + compare(initial.heightMapFile, "") + compare(initial.maxXValue, 10.0) + compare(initial.maxZValue, 10.0) + compare(initial.minXValue, 0) + compare(initial.minZValue, 0) + + compare(initial.columnCount, 0) + compare(initial.rowCount, 0) + verify(!initial.series) + + compare(initial.type, AbstractDataProxy.DataTypeSurface) + } + } + + TestCase { + name: "HeightMapSurfaceDataProxy Initialized" + + function test_initialized() { + compare(initialized.heightMapFile, ":/customtexture.jpg") + compare(initialized.maxXValue, 10.0) + compare(initialized.maxZValue, 10.0) + compare(initialized.minXValue, -10.0) + compare(initialized.minZValue, -10.0) + + compare(initialized.columnCount, 24) + compare(initialized.rowCount, 24) + } + } + + TestCase { + name: "HeightMapSurfaceDataProxy Change" + + function test_1_change() { + change.heightMapFile = ":/customtexture.jpg" + change.maxXValue = 10.0 + change.maxZValue = 10.0 + change.minXValue = -10.0 + change.minZValue = -10.0 + } + + function test_2_test_change() { + // This test has a dependency to the previous one due to asynchronous item model resolving + compare(change.heightMapFile, ":/customtexture.jpg") + compare(change.maxXValue, 10.0) + compare(change.maxZValue, 10.0) + compare(change.minXValue, -10.0) + compare(change.minZValue, -10.0) + + compare(change.columnCount, 24) + compare(change.rowCount, 24) + } + } +} diff --git a/tests/auto/qmltest/surface3d/tst_proxy.qml b/tests/auto/qmltest/surface3d/tst_proxy.qml index 25753a3f..8f353153 100644 --- a/tests/auto/qmltest/surface3d/tst_proxy.qml +++ b/tests/auto/qmltest/surface3d/tst_proxy.qml @@ -25,5 +25,239 @@ Item { height: 150 width: 150 - // TODO: Add tests for ItemModelSurfaceDataProxy and HeightMapSurfaceDataProxy + ItemModelSurfaceDataProxy { + id: initial + } + + ItemModelSurfaceDataProxy { + id: initialized + + autoColumnCategories: false + autoRowCategories: false + columnCategories: ["colcat1", "colcat2"] + columnRole: "col" + columnRolePattern: /^.*-(\d\d)$/ + columnRoleReplace: "\\1" + itemModel: ListModel { objectName: "model1" } + multiMatchBehavior: ItemModelSurfaceDataProxy.MMBAverage + rowCategories: ["rowcat1", "rowcat2"] + rowRole: "row" + rowRolePattern: /^(\d\d\d\d).*$/ + rowRoleReplace: "\\1" + xPosRole: "x" + xPosRolePattern: /^.*-(\d\d)$/ + xPosRoleReplace: "\\1" + yPosRole: "y" + yPosRolePattern: /^(\d\d\d\d).*$/ + yPosRoleReplace: "\\1" + zPosRole: "z" + zPosRolePattern: /-/ + zPosRoleReplace: "\\1" + } + + ItemModelSurfaceDataProxy { + id: change + } + + TestCase { + name: "ItemModelSurfaceDataProxy Initial" + + function test_initial() { + compare(initial.autoColumnCategories, true) + compare(initial.autoRowCategories, true) + compare(initial.columnCategories, []) + compare(initial.columnRole, "") + verify(initial.columnRolePattern) + compare(initial.columnRoleReplace, "") + verify(!initial.itemModel) + compare(initial.multiMatchBehavior, ItemModelSurfaceDataProxy.MMBLast) + compare(initial.rowCategories, []) + compare(initial.rowRole, "") + verify(initial.rowRolePattern) + compare(initial.rowRoleReplace, "") + compare(initial.useModelCategories, false) + compare(initial.xPosRole, "") + verify(initial.xPosRolePattern) + compare(initial.xPosRoleReplace, "") + compare(initial.yPosRole, "") + verify(initial.yPosRolePattern) + compare(initial.yPosRoleReplace, "") + compare(initial.zPosRole, "") + verify(initial.zPosRolePattern) + compare(initial.zPosRoleReplace, "") + + compare(initial.columnCount, 0) + compare(initial.rowCount, 0) + verify(!initial.series) + + compare(initial.type, AbstractDataProxy.DataTypeSurface) + } + } + + TestCase { + name: "ItemModelSurfaceDataProxy Initialized" + + function test_initialized() { + compare(initialized.autoColumnCategories, false) + compare(initialized.autoRowCategories, false) + compare(initialized.columnCategories.length, 2) + compare(initialized.columnCategories[0], "colcat1") + compare(initialized.columnCategories[1], "colcat2") + compare(initialized.columnRole, "col") + compare(initialized.columnRolePattern, /^.*-(\d\d)$/) + compare(initialized.columnRoleReplace, "\\1") + compare(initialized.itemModel.objectName, "model1") + compare(initialized.multiMatchBehavior, ItemModelSurfaceDataProxy.MMBAverage) + compare(initialized.rowCategories.length, 2) + compare(initialized.rowCategories[0], "rowcat1") + compare(initialized.rowCategories[1], "rowcat2") + compare(initialized.rowRole, "row") + compare(initialized.rowRolePattern, /^(\d\d\d\d).*$/) + compare(initialized.rowRoleReplace, "\\1") + compare(initialized.xPosRole, "x") + compare(initialized.xPosRolePattern, /^.*-(\d\d)$/) + compare(initialized.xPosRoleReplace, "\\1") + compare(initialized.yPosRole, "y") + compare(initialized.yPosRolePattern, /^(\d\d\d\d).*$/) + compare(initialized.yPosRoleReplace, "\\1") + compare(initialized.zPosRole, "z") + compare(initialized.zPosRolePattern, /-/) + compare(initialized.zPosRoleReplace, "\\1") + + compare(initialized.columnCount, 2) + compare(initialized.rowCount, 2) + } + } + + TestCase { + name: "ItemModelSurfaceDataProxy Change" + + ListModel { id: model1; objectName: "model1" } + + function test_1_change() { + change.autoColumnCategories = false + change.autoRowCategories = false + change.columnCategories = ["colcat1", "colcat2"] + change.columnRole = "col" + change.columnRolePattern = /^.*-(\d\d)$/ + change.columnRoleReplace = "\\1" + change.itemModel = model1 + change.multiMatchBehavior = ItemModelSurfaceDataProxy.MMBAverage + change.rowCategories = ["rowcat1", "rowcat2"] + change.rowRole = "row" + change.rowRolePattern = /^(\d\d\d\d).*$/ + change.rowRoleReplace = "\\1" + change.useModelCategories = true // Overwrites columnLabels and rowLabels + change.xPosRole = "x" + change.xPosRolePattern = /^.*-(\d\d)$/ + change.xPosRoleReplace = "\\1" + change.yPosRole = "y" + change.yPosRolePattern = /^(\d\d\d\d).*$/ + change.yPosRoleReplace = "\\1" + change.zPosRole = "z" + change.zPosRolePattern = /-/ + change.zPosRoleReplace = "\\1" + } + + function test_2_test_change() { + // This test has a dependency to the previous one due to asynchronous item model resolving + compare(change.autoColumnCategories, false) + compare(change.autoRowCategories, false) + compare(change.columnCategories.length, 2) + compare(change.columnCategories[0], "colcat1") + compare(change.columnCategories[1], "colcat2") + compare(change.columnRole, "col") + compare(change.columnRolePattern, /^.*-(\d\d)$/) + compare(change.columnRoleReplace, "\\1") + compare(change.itemModel.objectName, "model1") + compare(change.multiMatchBehavior, ItemModelSurfaceDataProxy.MMBAverage) + compare(change.rowCategories.length, 2) + compare(change.rowCategories[0], "rowcat1") + compare(change.rowCategories[1], "rowcat2") + compare(change.rowRole, "row") + compare(change.rowRolePattern, /^(\d\d\d\d).*$/) + compare(change.rowRoleReplace, "\\1") + compare(change.useModelCategories, true) + compare(change.xPosRole, "x") + compare(change.xPosRolePattern, /^.*-(\d\d)$/) + compare(change.xPosRoleReplace, "\\1") + compare(change.yPosRole, "y") + compare(change.yPosRolePattern, /^(\d\d\d\d).*$/) + compare(change.yPosRoleReplace, "\\1") + compare(change.zPosRole, "z") + compare(change.zPosRolePattern, /-/) + compare(change.zPosRoleReplace, "\\1") + + compare(change.columnCount, 0) + compare(change.rowCount, 0) + } + } + + TestCase { + name: "ItemModelSurfaceDataProxy MultiMatchBehaviour" + + Surface3D { + id: surface1 + Surface3DSeries { + ItemModelSurfaceDataProxy { + id: surfaceProxy + itemModel: ListModel { + ListElement{ coords: "0,0"; data: "5"; } + ListElement{ coords: "0,0"; data: "15"; } + ListElement{ coords: "0,1"; data: "5"; } + ListElement{ coords: "0,1"; data: "15"; } + ListElement{ coords: "1,0"; data: "5"; } + ListElement{ coords: "1,0"; data: "15"; } + ListElement{ coords: "1,1"; data: "0"; } + } + rowRole: "coords" + columnRole: "coords" + yPosRole: "data" + rowRolePattern: /(\d),\d/ + columnRolePattern: /(\d),(\d)/ + rowRoleReplace: "\\1" + columnRoleReplace: "\\2" + } + } + } + + function test_0_async_dummy() { + } + + function test_1_test_multimatch() { + compare(surface1.axisY.max, 15) + } + + function test_2_multimatch() { + surfaceProxy.multiMatchBehavior = ItemModelSurfaceDataProxy.MMBFirst + } + + function test_3_test_multimatch() { + compare(surface1.axisY.max, 5) + } + + function test_4_multimatch() { + surfaceProxy.multiMatchBehavior = ItemModelSurfaceDataProxy.MMBLast + } + + function test_5_test_multimatch() { + compare(surface1.axisY.max, 15) + } + + function test_6_multimatch() { + surfaceProxy.multiMatchBehavior = ItemModelSurfaceDataProxy.MMBAverage + } + + function test_7_test_multimatch() { + compare(surface1.axisY.max, 10) + } + + function test_8_multimatch() { + surfaceProxy.multiMatchBehavior = ItemModelSurfaceDataProxy.MMBCumulativeY + } + + function test_9_test_multimatch() { + compare(surface1.axisY.max, 20) + } + } } -- cgit v1.2.3 From 425ece960c378f8486d0c5e544cc7d0b95c9f728 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Thu, 16 Oct 2014 14:54:53 +0300 Subject: Fix memory leaks in volumetrictest app. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I742a5a5d7c69ef73ba469bfa4bac49a71b8f578a Reviewed-by: Mika Salmela Reviewed-by: Tomi Korpipää --- tests/volumetrictest/volumetrictest.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/volumetrictest/volumetrictest.cpp b/tests/volumetrictest/volumetrictest.cpp index dadb86be..24ecac3d 100644 --- a/tests/volumetrictest/volumetrictest.cpp +++ b/tests/volumetrictest/volumetrictest.cpp @@ -381,6 +381,9 @@ void VolumetricModifier::createVolume() m_volumeItem->createTextureData(imageArray); + for (int i = 0; i < imageCount; i++) + delete imageArray[i]; + m_sliceIndexX = m_volumeItem->textureWidth() / 2; m_sliceIndexY = m_volumeItem->textureWidth() / 2; m_sliceIndexZ = m_volumeItem->textureWidth() / 2; @@ -534,6 +537,9 @@ void VolumetricModifier::createAnotherVolume() m_volumeItem2->createTextureData(imageArray); + for (int i = 0; i < imageCount; i++) + delete imageArray[i]; + m_sliceIndexX = m_volumeItem2->textureWidth() / 2; m_sliceIndexY = m_volumeItem2->textureWidth() / 2; m_sliceIndexZ = m_volumeItem2->textureWidth() / 2; -- cgit v1.2.3 From b25f6926ded030959d1931ec0a834b1a9e5f27e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomi=20Korpip=C3=A4=C3=A4?= Date: Fri, 17 Oct 2014 08:50:12 +0300 Subject: Added invalid value tests for QML autotests Plus fixed a QML documentation bug, and added a missing inheritance to docs. Task-number: QTRD-3383 Change-Id: I1f7c56c49b829fee2cfd0731bbcc7e248a06a0c0 Reviewed-by: Miikka Heikkinen --- .../axis/qlogvalue3daxisformatter.cpp | 1 + src/datavisualization/theme/q3dtheme.cpp | 4 ++-- tests/auto/qmltest/axis3d/tst_category.qml | 19 +++++++++++++++++ tests/auto/qmltest/axis3d/tst_logvalue.qml | 15 ++++++++++++++ tests/auto/qmltest/axis3d/tst_value.qml | 24 ++++++++++++++++++++++ tests/auto/qmltest/custom3d/tst_customvolume.qml | 19 +++++++++++++++++ tests/auto/qmltest/scatter3d/tst_scatterseries.qml | 14 +++++++++++++ tests/auto/qmltest/scene3d/tst_camera.qml | 16 +++++++++++++++ tests/auto/qmltest/scene3d/tst_scene.qml | 13 ++++++++++++ tests/auto/qmltest/surface3d/tst_heightproxy.qml | 15 ++++++++++++++ tests/auto/qmltest/theme3d/tst_theme.qml | 24 ++++++++++++++++++++++ 11 files changed, 162 insertions(+), 2 deletions(-) diff --git a/src/datavisualization/axis/qlogvalue3daxisformatter.cpp b/src/datavisualization/axis/qlogvalue3daxisformatter.cpp index 7367e7c5..85fd5c4f 100644 --- a/src/datavisualization/axis/qlogvalue3daxisformatter.cpp +++ b/src/datavisualization/axis/qlogvalue3daxisformatter.cpp @@ -42,6 +42,7 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION * \since QtDataVisualization 1.1 * \ingroup datavisualization_qml * \instantiates QLogValue3DAxisFormatter + * \inherits ValueAxis3DFormatter * \brief LogValueAxis3DFormatter implements logarithmic value axis formatter. * * This type provides formatting rules for a logarithmic ValueAxis3D. diff --git a/src/datavisualization/theme/q3dtheme.cpp b/src/datavisualization/theme/q3dtheme.cpp index 70397e23..a1dfe8b3 100644 --- a/src/datavisualization/theme/q3dtheme.cpp +++ b/src/datavisualization/theme/q3dtheme.cpp @@ -323,7 +323,7 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION /*! * \qmlproperty real Theme3D::lightStrength * - * Specular light strength for the whole graph. Value must be between 0.0 and 1.0. + * Specular light strength for the whole graph. Value must be between 0.0 and 10.0. */ /*! @@ -335,7 +335,7 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION /*! * \qmlproperty real Theme3D::highlightLightStrength * - * Specular light strength for highlighted objects. Value must be between 0.0 and 1.0. + * Specular light strength for highlighted objects. Value must be between 0.0 and 10.0. */ /*! diff --git a/tests/auto/qmltest/axis3d/tst_category.qml b/tests/auto/qmltest/axis3d/tst_category.qml index 7f0be845..318fa011 100644 --- a/tests/auto/qmltest/axis3d/tst_category.qml +++ b/tests/auto/qmltest/axis3d/tst_category.qml @@ -46,6 +46,10 @@ Item { id: change } + CategoryAxis3D { + id: invalid + } + TestCase { name: "CategoryAxis3D Initial" @@ -113,4 +117,19 @@ Item { compare(change.titleVisible, true) } } + + TestCase { + name: "CategoryAxis3D Invalid" + + function test_invalid() { + invalid.labelAutoRotation = -10 + compare(invalid.labelAutoRotation, 0.0) + invalid.labelAutoRotation = 100 + compare(invalid.labelAutoRotation, 90.0) + invalid.max = -10 + compare(invalid.min, 0) + invalid.min = 10 + compare(invalid.max, 11) + } + } } diff --git a/tests/auto/qmltest/axis3d/tst_logvalue.qml b/tests/auto/qmltest/axis3d/tst_logvalue.qml index fad93611..89228ad1 100644 --- a/tests/auto/qmltest/axis3d/tst_logvalue.qml +++ b/tests/auto/qmltest/axis3d/tst_logvalue.qml @@ -40,6 +40,10 @@ Item { id: change } + LogValueAxis3DFormatter { + id: invalid + } + TestCase { name: "LogValueAxis3DFormatter Initial" @@ -73,4 +77,15 @@ Item { compare(change.showEdgeLabels, false) } } + + TestCase { + name: "LogValueAxis3DFormatter Invalid" + + function test_invalid() { + invalid.base = 1 + compare(invalid.base, 10) + invalid.base = -1 + compare(invalid.base, 10) + } + } } diff --git a/tests/auto/qmltest/axis3d/tst_value.qml b/tests/auto/qmltest/axis3d/tst_value.qml index 6a8f7849..54189c8b 100644 --- a/tests/auto/qmltest/axis3d/tst_value.qml +++ b/tests/auto/qmltest/axis3d/tst_value.qml @@ -50,6 +50,10 @@ Item { id: change } + ValueAxis3D { + id: invalid + } + TestCase { name: "ValueAxis3D Initial" @@ -127,4 +131,24 @@ Item { compare(change.titleVisible, true) } } + + TestCase { + name: "ValueAxis3D Invalid" + + function test_invalid() { + invalid.segmentCount = -1 + compare(invalid.segmentCount, 1) + invalid.subSegmentCount = -1 + compare(invalid.subSegmentCount, 1) + + invalid.labelAutoRotation = -10 + compare(invalid.labelAutoRotation, 0.0) + invalid.labelAutoRotation = 100 + compare(invalid.labelAutoRotation, 90.0) + invalid.max = -10 + compare(invalid.min, -11) + invalid.min = 10 + compare(invalid.max, 11) + } + } } diff --git a/tests/auto/qmltest/custom3d/tst_customvolume.qml b/tests/auto/qmltest/custom3d/tst_customvolume.qml index 89717d5d..08c15013 100644 --- a/tests/auto/qmltest/custom3d/tst_customvolume.qml +++ b/tests/auto/qmltest/custom3d/tst_customvolume.qml @@ -57,6 +57,10 @@ Item { id: change } + Custom3DVolume { + id: invalid + } + TestCase { name: "Custom3DVolume Initial" @@ -160,4 +164,19 @@ Item { compare(change.visible, false) } } + + TestCase { + name: "Custom3DVolume Invalid" + + function test_invalid() { + invalid.alphaMultiplier = -1.0 + compare(invalid.alphaMultiplier, 1.0) + invalid.sliceFrameGaps = Qt.vector3d(-0.1, -0.1, -0.1) + compare(invalid.sliceFrameGaps, Qt.vector3d(0.01, 0.01, 0.01)) + invalid.sliceFrameThicknesses = Qt.vector3d(-0.1, -0.1, -0.1) + compare(invalid.sliceFrameThicknesses, Qt.vector3d(0.01, 0.01, 0.01)) + invalid.sliceFrameWidths = Qt.vector3d(-0.1, -0.1, -0.1) + compare(invalid.sliceFrameWidths, Qt.vector3d(0.01, 0.01, 0.01)) + } + } } diff --git a/tests/auto/qmltest/scatter3d/tst_scatterseries.qml b/tests/auto/qmltest/scatter3d/tst_scatterseries.qml index e42443ad..4df58303 100644 --- a/tests/auto/qmltest/scatter3d/tst_scatterseries.qml +++ b/tests/auto/qmltest/scatter3d/tst_scatterseries.qml @@ -100,6 +100,10 @@ Item { id: change } + Scatter3DSeries { + id: invalid + } + TestCase { name: "Scatter3DSeries Initial" @@ -216,4 +220,14 @@ Item { compare(change.baseGradient.stops[0].color, "#ffff00") } } + TestCase { + name: "Scatter3DSeries Invalid" + + function test_invalid() { + invalid.itemSize = -1.0 + compare(invalid.itemSize, 0.0) + invalid.itemSize = 1.1 + compare(invalid.itemSize, 0.0) + } + } } diff --git a/tests/auto/qmltest/scene3d/tst_camera.qml b/tests/auto/qmltest/scene3d/tst_camera.qml index c29de0c7..07adf633 100644 --- a/tests/auto/qmltest/scene3d/tst_camera.qml +++ b/tests/auto/qmltest/scene3d/tst_camera.qml @@ -45,6 +45,10 @@ Item { id: change } + Camera3D { + id: invalid + } + TestCase { name: "Camera3D Initial" @@ -114,6 +118,18 @@ Item { compare(change.yRotation, 22.5) compare(change.zoomLevel, 500.0) } + } + TestCase { + name: "Camera3D Invalid" + + function test_invalid() { + invalid.target = Qt.vector3d(-1.5, -1.5, -1.5) + compare(invalid.target, Qt.vector3d(-1.0, -1.0, -1.0)) + invalid.target = Qt.vector3d(1.5, 1.5, 1.5) + compare(invalid.target, Qt.vector3d(1.0, 1.0, 1.0)) + invalid.minZoomLevel = 0.1 + compare(invalid.minZoomLevel, 1.0) + } } } diff --git a/tests/auto/qmltest/scene3d/tst_scene.qml b/tests/auto/qmltest/scene3d/tst_scene.qml index 8d497b5c..d53042ca 100644 --- a/tests/auto/qmltest/scene3d/tst_scene.qml +++ b/tests/auto/qmltest/scene3d/tst_scene.qml @@ -46,6 +46,10 @@ Item { id: change } + Bars3D { + id: invalid + } + TestCase { name: "Scene3D Initial" @@ -109,4 +113,13 @@ Item { compare(change.scene.viewport, Qt.rect(0, 0, 100, 100)) } } + + TestCase { + name: "Scene3D Invalid" + + function test_invalid() { + invalid.scene.primarySubViewport = Qt.rect(0, 0, -50, -50) + compare(invalid.scene.primarySubViewport, Qt.rect(0, 0, 0, 0)) + } + } } diff --git a/tests/auto/qmltest/surface3d/tst_heightproxy.qml b/tests/auto/qmltest/surface3d/tst_heightproxy.qml index dc6938f8..29772451 100644 --- a/tests/auto/qmltest/surface3d/tst_heightproxy.qml +++ b/tests/auto/qmltest/surface3d/tst_heightproxy.qml @@ -42,6 +42,10 @@ Item { id: change } + HeightMapSurfaceDataProxy { + id: invalid + } + TestCase { name: "HeightMapSurfaceDataProxy Initial" @@ -98,4 +102,15 @@ Item { compare(change.rowCount, 24) } } + + TestCase { + name: "HeightMapSurfaceDataProxy Invalid" + + function test_invalid() { + invalid.maxXValue = -10 + compare(invalid.minXValue, -11) + invalid.minZValue = 20 + compare(invalid.maxZValue, 21) + } + } } diff --git a/tests/auto/qmltest/theme3d/tst_theme.qml b/tests/auto/qmltest/theme3d/tst_theme.qml index 9c1a22f1..3e42b300 100644 --- a/tests/auto/qmltest/theme3d/tst_theme.qml +++ b/tests/auto/qmltest/theme3d/tst_theme.qml @@ -93,6 +93,10 @@ Item { id: change } + Theme3D { + id: invalid + } + TestCase { name: "Theme3D Initial" @@ -239,4 +243,24 @@ Item { compare(change.baseGradients[0].stops[0].color, "#000000") } } + + + TestCase { + name: "Theme3D Invalid" + + function test_invalid() { + invalid.ambientLightStrength = -1.0 + compare(invalid.ambientLightStrength, 0.25) + invalid.ambientLightStrength = 1.1 + compare(invalid.ambientLightStrength, 0.25) + invalid.highlightLightStrength = -1.0 + compare(invalid.highlightLightStrength, 7.5) + invalid.highlightLightStrength = 10.1 + compare(invalid.highlightLightStrength, 7.5) + invalid.lightStrength = -1.0 + compare(invalid.lightStrength, 5.0) + invalid.lightStrength = 10.1 + compare(invalid.lightStrength, 5.0) + } + } } -- cgit v1.2.3 From b5fdf5e642a8fe30725bd0eba4ed48c69fdb2980 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomi=20Korpip=C3=A4=C3=A4?= Date: Fri, 17 Oct 2014 09:08:13 +0300 Subject: Qt 5.4 qml type fix Fixes missing QML Types in assistant. Change-Id: Id79c3f281ec4fcc9bd9816ec731ff654e61b3e04 Change-Id: Id79c3f281ec4fcc9bd9816ec731ff654e61b3e04 Reviewed-by: Miikka Heikkinen --- src/datavisualization/doc/qtdatavisualization.qdocconf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/datavisualization/doc/qtdatavisualization.qdocconf b/src/datavisualization/doc/qtdatavisualization.qdocconf index 6e9f09b4..6c4b44d9 100644 --- a/src/datavisualization/doc/qtdatavisualization.qdocconf +++ b/src/datavisualization/doc/qtdatavisualization.qdocconf @@ -50,7 +50,7 @@ qhp.QtDataVisualization.subprojects.classes.selectors = class qhp.QtDataVisualization.subprojects.classes.sortPages = true qhp.QtDataVisualization.subprojects.types.title = QML Types qhp.QtDataVisualization.subprojects.types.indexTitle = Qt Data Visualization QML Types -qhp.QtDataVisualization.subprojects.types.selectors = fake:qmlclass +qhp.QtDataVisualization.subprojects.types.selectors = qmlclass qhp.QtDataVisualization.subprojects.types.sortPages = true navigation.landingpage = Qt Data Visualization -- cgit v1.2.3 From dcbfe9d4c645778b93e8716c95c3e31163c76292 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomi=20Korpip=C3=A4=C3=A4?= Date: Fri, 17 Oct 2014 10:36:24 +0300 Subject: Added skeletons for C++ graph proxies and series Task-number: QTRD-3368 Change-Id: I85680bfca28e5ed1039526310fdf89717b1e84ae Reviewed-by: Miikka Heikkinen --- tests/auto/cpptest/cpptest.pro | 12 ++- .../q3dbars-modelproxy/q3dbars-modelproxy.pro | 8 ++ .../auto/cpptest/q3dbars-modelproxy/tst_proxy.cpp | 85 ++++++++++++++++++++++ tests/auto/cpptest/q3dbars-proxy/q3dbars-proxy.pro | 8 ++ tests/auto/cpptest/q3dbars-proxy/tst_proxy.cpp | 85 ++++++++++++++++++++++ .../auto/cpptest/q3dbars-series/q3dbars-series.pro | 8 ++ tests/auto/cpptest/q3dbars-series/tst_series.cpp | 85 ++++++++++++++++++++++ .../q3dscatter-modelproxy.pro | 8 ++ .../cpptest/q3dscatter-modelproxy/tst_proxy.cpp | 85 ++++++++++++++++++++++ .../cpptest/q3dscatter-proxy/q3dscatter-proxy.pro | 8 ++ tests/auto/cpptest/q3dscatter-proxy/tst_proxy.cpp | 85 ++++++++++++++++++++++ .../q3dscatter-series/q3dscatter-series.pro | 8 ++ .../auto/cpptest/q3dscatter-series/tst_series.cpp | 85 ++++++++++++++++++++++ .../q3dsurface-heightproxy.pro | 8 ++ .../cpptest/q3dsurface-heightproxy/tst_proxy.cpp | 85 ++++++++++++++++++++++ .../q3dsurface-modelproxy.pro | 8 ++ .../cpptest/q3dsurface-modelproxy/tst_proxy.cpp | 85 ++++++++++++++++++++++ .../cpptest/q3dsurface-proxy/q3dsurface-proxy.pro | 8 ++ tests/auto/cpptest/q3dsurface-proxy/tst_proxy.cpp | 85 ++++++++++++++++++++++ .../q3dsurface-series/q3dsurface-series.pro | 8 ++ .../auto/cpptest/q3dsurface-series/tst_series.cpp | 85 ++++++++++++++++++++++ 21 files changed, 941 insertions(+), 1 deletion(-) create mode 100644 tests/auto/cpptest/q3dbars-modelproxy/q3dbars-modelproxy.pro create mode 100644 tests/auto/cpptest/q3dbars-modelproxy/tst_proxy.cpp create mode 100644 tests/auto/cpptest/q3dbars-proxy/q3dbars-proxy.pro create mode 100644 tests/auto/cpptest/q3dbars-proxy/tst_proxy.cpp create mode 100644 tests/auto/cpptest/q3dbars-series/q3dbars-series.pro create mode 100644 tests/auto/cpptest/q3dbars-series/tst_series.cpp create mode 100644 tests/auto/cpptest/q3dscatter-modelproxy/q3dscatter-modelproxy.pro create mode 100644 tests/auto/cpptest/q3dscatter-modelproxy/tst_proxy.cpp create mode 100644 tests/auto/cpptest/q3dscatter-proxy/q3dscatter-proxy.pro create mode 100644 tests/auto/cpptest/q3dscatter-proxy/tst_proxy.cpp create mode 100644 tests/auto/cpptest/q3dscatter-series/q3dscatter-series.pro create mode 100644 tests/auto/cpptest/q3dscatter-series/tst_series.cpp create mode 100644 tests/auto/cpptest/q3dsurface-heightproxy/q3dsurface-heightproxy.pro create mode 100644 tests/auto/cpptest/q3dsurface-heightproxy/tst_proxy.cpp create mode 100644 tests/auto/cpptest/q3dsurface-modelproxy/q3dsurface-modelproxy.pro create mode 100644 tests/auto/cpptest/q3dsurface-modelproxy/tst_proxy.cpp create mode 100644 tests/auto/cpptest/q3dsurface-proxy/q3dsurface-proxy.pro create mode 100644 tests/auto/cpptest/q3dsurface-proxy/tst_proxy.cpp create mode 100644 tests/auto/cpptest/q3dsurface-series/q3dsurface-series.pro create mode 100644 tests/auto/cpptest/q3dsurface-series/tst_series.cpp diff --git a/tests/auto/cpptest/cpptest.pro b/tests/auto/cpptest/cpptest.pro index 6d52a766..29ba3dc3 100644 --- a/tests/auto/cpptest/cpptest.pro +++ b/tests/auto/cpptest/cpptest.pro @@ -1,4 +1,14 @@ TEMPLATE = subdirs SUBDIRS = q3dbars \ + q3dbars-proxy \ + q3dbars-modelproxy \ + q3dbars-series \ q3dscatter \ - q3dsurface + q3dscatter-proxy \ + q3dscatter-modelproxy \ + q3dscatter-series \ + q3dsurface \ + q3dsurface-proxy \ + q3dsurface-modelproxy \ + q3dsurface-heightproxy \ + q3dsurface-series diff --git a/tests/auto/cpptest/q3dbars-modelproxy/q3dbars-modelproxy.pro b/tests/auto/cpptest/q3dbars-modelproxy/q3dbars-modelproxy.pro new file mode 100644 index 00000000..b0b5d361 --- /dev/null +++ b/tests/auto/cpptest/q3dbars-modelproxy/q3dbars-modelproxy.pro @@ -0,0 +1,8 @@ +QT += testlib datavisualization + +TARGET = tst_cpptest +CONFIG += console testcase + +TEMPLATE = app + +SOURCES += tst_proxy.cpp diff --git a/tests/auto/cpptest/q3dbars-modelproxy/tst_proxy.cpp b/tests/auto/cpptest/q3dbars-modelproxy/tst_proxy.cpp new file mode 100644 index 00000000..9c54df53 --- /dev/null +++ b/tests/auto/cpptest/q3dbars-modelproxy/tst_proxy.cpp @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +#include + +#include + +using namespace QtDataVisualization; + +class tst_proxy: public QObject +{ + Q_OBJECT + +private slots: + void initTestCase(); + void cleanupTestCase(); + void init(); + void cleanup(); + + void construct(); + + void initialProperties(); + void initializeProperties(); + void invalidProperties(); + +private: + QItemModelBarDataProxy *m_proxy; +}; + +void tst_proxy::initTestCase() +{ +} + +void tst_proxy::cleanupTestCase() +{ +} + +void tst_proxy::init() +{ + m_proxy = new QItemModelBarDataProxy(); +} + +void tst_proxy::cleanup() +{ + delete m_proxy; +} + +void tst_proxy::construct() +{ + QItemModelBarDataProxy *proxy = new QItemModelBarDataProxy(); + QVERIFY(proxy); + delete proxy; +} + +void tst_proxy::initialProperties() +{ + QVERIFY(m_proxy); +} + +void tst_proxy::initializeProperties() +{ + QVERIFY(m_proxy); +} + +void tst_proxy::invalidProperties() +{ +} + +QTEST_MAIN(tst_proxy) +#include "tst_proxy.moc" diff --git a/tests/auto/cpptest/q3dbars-proxy/q3dbars-proxy.pro b/tests/auto/cpptest/q3dbars-proxy/q3dbars-proxy.pro new file mode 100644 index 00000000..b0b5d361 --- /dev/null +++ b/tests/auto/cpptest/q3dbars-proxy/q3dbars-proxy.pro @@ -0,0 +1,8 @@ +QT += testlib datavisualization + +TARGET = tst_cpptest +CONFIG += console testcase + +TEMPLATE = app + +SOURCES += tst_proxy.cpp diff --git a/tests/auto/cpptest/q3dbars-proxy/tst_proxy.cpp b/tests/auto/cpptest/q3dbars-proxy/tst_proxy.cpp new file mode 100644 index 00000000..d152c46b --- /dev/null +++ b/tests/auto/cpptest/q3dbars-proxy/tst_proxy.cpp @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +#include + +#include + +using namespace QtDataVisualization; + +class tst_proxy: public QObject +{ + Q_OBJECT + +private slots: + void initTestCase(); + void cleanupTestCase(); + void init(); + void cleanup(); + + void construct(); + + void initialProperties(); + void initializeProperties(); + void invalidProperties(); + +private: + QBarDataProxy *m_proxy; +}; + +void tst_proxy::initTestCase() +{ +} + +void tst_proxy::cleanupTestCase() +{ +} + +void tst_proxy::init() +{ + m_proxy = new QBarDataProxy(); +} + +void tst_proxy::cleanup() +{ + delete m_proxy; +} + +void tst_proxy::construct() +{ + QBarDataProxy *proxy = new QBarDataProxy(); + QVERIFY(proxy); + delete proxy; +} + +void tst_proxy::initialProperties() +{ + QVERIFY(m_proxy); +} + +void tst_proxy::initializeProperties() +{ + QVERIFY(m_proxy); +} + +void tst_proxy::invalidProperties() +{ +} + +QTEST_MAIN(tst_proxy) +#include "tst_proxy.moc" diff --git a/tests/auto/cpptest/q3dbars-series/q3dbars-series.pro b/tests/auto/cpptest/q3dbars-series/q3dbars-series.pro new file mode 100644 index 00000000..481653ef --- /dev/null +++ b/tests/auto/cpptest/q3dbars-series/q3dbars-series.pro @@ -0,0 +1,8 @@ +QT += testlib datavisualization + +TARGET = tst_cpptest +CONFIG += console testcase + +TEMPLATE = app + +SOURCES += tst_series.cpp diff --git a/tests/auto/cpptest/q3dbars-series/tst_series.cpp b/tests/auto/cpptest/q3dbars-series/tst_series.cpp new file mode 100644 index 00000000..706baf94 --- /dev/null +++ b/tests/auto/cpptest/q3dbars-series/tst_series.cpp @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +#include + +#include + +using namespace QtDataVisualization; + +class tst_series: public QObject +{ + Q_OBJECT + +private slots: + void initTestCase(); + void cleanupTestCase(); + void init(); + void cleanup(); + + void construct(); + + void initialProperties(); + void initializeProperties(); + void invalidProperties(); + +private: + QBar3DSeries *m_series; +}; + +void tst_series::initTestCase() +{ +} + +void tst_series::cleanupTestCase() +{ +} + +void tst_series::init() +{ + m_series = new QBar3DSeries(); +} + +void tst_series::cleanup() +{ + delete m_series; +} + +void tst_series::construct() +{ + QBar3DSeries *series = new QBar3DSeries(); + QVERIFY(series); + delete series; +} + +void tst_series::initialProperties() +{ + QVERIFY(m_series); +} + +void tst_series::initializeProperties() +{ + QVERIFY(m_series); +} + +void tst_series::invalidProperties() +{ +} + +QTEST_MAIN(tst_series) +#include "tst_series.moc" diff --git a/tests/auto/cpptest/q3dscatter-modelproxy/q3dscatter-modelproxy.pro b/tests/auto/cpptest/q3dscatter-modelproxy/q3dscatter-modelproxy.pro new file mode 100644 index 00000000..b0b5d361 --- /dev/null +++ b/tests/auto/cpptest/q3dscatter-modelproxy/q3dscatter-modelproxy.pro @@ -0,0 +1,8 @@ +QT += testlib datavisualization + +TARGET = tst_cpptest +CONFIG += console testcase + +TEMPLATE = app + +SOURCES += tst_proxy.cpp diff --git a/tests/auto/cpptest/q3dscatter-modelproxy/tst_proxy.cpp b/tests/auto/cpptest/q3dscatter-modelproxy/tst_proxy.cpp new file mode 100644 index 00000000..7a4a78be --- /dev/null +++ b/tests/auto/cpptest/q3dscatter-modelproxy/tst_proxy.cpp @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +#include + +#include + +using namespace QtDataVisualization; + +class tst_proxy: public QObject +{ + Q_OBJECT + +private slots: + void initTestCase(); + void cleanupTestCase(); + void init(); + void cleanup(); + + void construct(); + + void initialProperties(); + void initializeProperties(); + void invalidProperties(); + +private: + QItemModelScatterDataProxy *m_proxy; +}; + +void tst_proxy::initTestCase() +{ +} + +void tst_proxy::cleanupTestCase() +{ +} + +void tst_proxy::init() +{ + m_proxy = new QItemModelScatterDataProxy(); +} + +void tst_proxy::cleanup() +{ + delete m_proxy; +} + +void tst_proxy::construct() +{ + QItemModelScatterDataProxy *proxy = new QItemModelScatterDataProxy(); + QVERIFY(proxy); + delete proxy; +} + +void tst_proxy::initialProperties() +{ + QVERIFY(m_proxy); +} + +void tst_proxy::initializeProperties() +{ + QVERIFY(m_proxy); +} + +void tst_proxy::invalidProperties() +{ +} + +QTEST_MAIN(tst_proxy) +#include "tst_proxy.moc" diff --git a/tests/auto/cpptest/q3dscatter-proxy/q3dscatter-proxy.pro b/tests/auto/cpptest/q3dscatter-proxy/q3dscatter-proxy.pro new file mode 100644 index 00000000..b0b5d361 --- /dev/null +++ b/tests/auto/cpptest/q3dscatter-proxy/q3dscatter-proxy.pro @@ -0,0 +1,8 @@ +QT += testlib datavisualization + +TARGET = tst_cpptest +CONFIG += console testcase + +TEMPLATE = app + +SOURCES += tst_proxy.cpp diff --git a/tests/auto/cpptest/q3dscatter-proxy/tst_proxy.cpp b/tests/auto/cpptest/q3dscatter-proxy/tst_proxy.cpp new file mode 100644 index 00000000..00bbadb4 --- /dev/null +++ b/tests/auto/cpptest/q3dscatter-proxy/tst_proxy.cpp @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +#include + +#include + +using namespace QtDataVisualization; + +class tst_proxy: public QObject +{ + Q_OBJECT + +private slots: + void initTestCase(); + void cleanupTestCase(); + void init(); + void cleanup(); + + void construct(); + + void initialProperties(); + void initializeProperties(); + void invalidProperties(); + +private: + QScatterDataProxy *m_proxy; +}; + +void tst_proxy::initTestCase() +{ +} + +void tst_proxy::cleanupTestCase() +{ +} + +void tst_proxy::init() +{ + m_proxy = new QScatterDataProxy(); +} + +void tst_proxy::cleanup() +{ + delete m_proxy; +} + +void tst_proxy::construct() +{ + QScatterDataProxy *proxy = new QScatterDataProxy(); + QVERIFY(proxy); + delete proxy; +} + +void tst_proxy::initialProperties() +{ + QVERIFY(m_proxy); +} + +void tst_proxy::initializeProperties() +{ + QVERIFY(m_proxy); +} + +void tst_proxy::invalidProperties() +{ +} + +QTEST_MAIN(tst_proxy) +#include "tst_proxy.moc" diff --git a/tests/auto/cpptest/q3dscatter-series/q3dscatter-series.pro b/tests/auto/cpptest/q3dscatter-series/q3dscatter-series.pro new file mode 100644 index 00000000..481653ef --- /dev/null +++ b/tests/auto/cpptest/q3dscatter-series/q3dscatter-series.pro @@ -0,0 +1,8 @@ +QT += testlib datavisualization + +TARGET = tst_cpptest +CONFIG += console testcase + +TEMPLATE = app + +SOURCES += tst_series.cpp diff --git a/tests/auto/cpptest/q3dscatter-series/tst_series.cpp b/tests/auto/cpptest/q3dscatter-series/tst_series.cpp new file mode 100644 index 00000000..80db0ac8 --- /dev/null +++ b/tests/auto/cpptest/q3dscatter-series/tst_series.cpp @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +#include + +#include + +using namespace QtDataVisualization; + +class tst_series: public QObject +{ + Q_OBJECT + +private slots: + void initTestCase(); + void cleanupTestCase(); + void init(); + void cleanup(); + + void construct(); + + void initialProperties(); + void initializeProperties(); + void invalidProperties(); + +private: + QScatter3DSeries *m_series; +}; + +void tst_series::initTestCase() +{ +} + +void tst_series::cleanupTestCase() +{ +} + +void tst_series::init() +{ + m_series = new QScatter3DSeries(); +} + +void tst_series::cleanup() +{ + delete m_series; +} + +void tst_series::construct() +{ + QScatter3DSeries *series = new QScatter3DSeries(); + QVERIFY(series); + delete series; +} + +void tst_series::initialProperties() +{ + QVERIFY(m_series); +} + +void tst_series::initializeProperties() +{ + QVERIFY(m_series); +} + +void tst_series::invalidProperties() +{ +} + +QTEST_MAIN(tst_series) +#include "tst_series.moc" diff --git a/tests/auto/cpptest/q3dsurface-heightproxy/q3dsurface-heightproxy.pro b/tests/auto/cpptest/q3dsurface-heightproxy/q3dsurface-heightproxy.pro new file mode 100644 index 00000000..b0b5d361 --- /dev/null +++ b/tests/auto/cpptest/q3dsurface-heightproxy/q3dsurface-heightproxy.pro @@ -0,0 +1,8 @@ +QT += testlib datavisualization + +TARGET = tst_cpptest +CONFIG += console testcase + +TEMPLATE = app + +SOURCES += tst_proxy.cpp diff --git a/tests/auto/cpptest/q3dsurface-heightproxy/tst_proxy.cpp b/tests/auto/cpptest/q3dsurface-heightproxy/tst_proxy.cpp new file mode 100644 index 00000000..cf896bbf --- /dev/null +++ b/tests/auto/cpptest/q3dsurface-heightproxy/tst_proxy.cpp @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +#include + +#include + +using namespace QtDataVisualization; + +class tst_proxy: public QObject +{ + Q_OBJECT + +private slots: + void initTestCase(); + void cleanupTestCase(); + void init(); + void cleanup(); + + void construct(); + + void initialProperties(); + void initializeProperties(); + void invalidProperties(); + +private: + QHeightMapSurfaceDataProxy *m_proxy; +}; + +void tst_proxy::initTestCase() +{ +} + +void tst_proxy::cleanupTestCase() +{ +} + +void tst_proxy::init() +{ + m_proxy = new QHeightMapSurfaceDataProxy(); +} + +void tst_proxy::cleanup() +{ + delete m_proxy; +} + +void tst_proxy::construct() +{ + QHeightMapSurfaceDataProxy *proxy = new QHeightMapSurfaceDataProxy(); + QVERIFY(proxy); + delete proxy; +} + +void tst_proxy::initialProperties() +{ + QVERIFY(m_proxy); +} + +void tst_proxy::initializeProperties() +{ + QVERIFY(m_proxy); +} + +void tst_proxy::invalidProperties() +{ +} + +QTEST_MAIN(tst_proxy) +#include "tst_proxy.moc" diff --git a/tests/auto/cpptest/q3dsurface-modelproxy/q3dsurface-modelproxy.pro b/tests/auto/cpptest/q3dsurface-modelproxy/q3dsurface-modelproxy.pro new file mode 100644 index 00000000..b0b5d361 --- /dev/null +++ b/tests/auto/cpptest/q3dsurface-modelproxy/q3dsurface-modelproxy.pro @@ -0,0 +1,8 @@ +QT += testlib datavisualization + +TARGET = tst_cpptest +CONFIG += console testcase + +TEMPLATE = app + +SOURCES += tst_proxy.cpp diff --git a/tests/auto/cpptest/q3dsurface-modelproxy/tst_proxy.cpp b/tests/auto/cpptest/q3dsurface-modelproxy/tst_proxy.cpp new file mode 100644 index 00000000..c7c1dd83 --- /dev/null +++ b/tests/auto/cpptest/q3dsurface-modelproxy/tst_proxy.cpp @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +#include + +#include + +using namespace QtDataVisualization; + +class tst_proxy: public QObject +{ + Q_OBJECT + +private slots: + void initTestCase(); + void cleanupTestCase(); + void init(); + void cleanup(); + + void construct(); + + void initialProperties(); + void initializeProperties(); + void invalidProperties(); + +private: + QItemModelSurfaceDataProxy *m_proxy; +}; + +void tst_proxy::initTestCase() +{ +} + +void tst_proxy::cleanupTestCase() +{ +} + +void tst_proxy::init() +{ + m_proxy = new QItemModelSurfaceDataProxy(); +} + +void tst_proxy::cleanup() +{ + delete m_proxy; +} + +void tst_proxy::construct() +{ + QItemModelSurfaceDataProxy *proxy = new QItemModelSurfaceDataProxy(); + QVERIFY(proxy); + delete proxy; +} + +void tst_proxy::initialProperties() +{ + QVERIFY(m_proxy); +} + +void tst_proxy::initializeProperties() +{ + QVERIFY(m_proxy); +} + +void tst_proxy::invalidProperties() +{ +} + +QTEST_MAIN(tst_proxy) +#include "tst_proxy.moc" diff --git a/tests/auto/cpptest/q3dsurface-proxy/q3dsurface-proxy.pro b/tests/auto/cpptest/q3dsurface-proxy/q3dsurface-proxy.pro new file mode 100644 index 00000000..b0b5d361 --- /dev/null +++ b/tests/auto/cpptest/q3dsurface-proxy/q3dsurface-proxy.pro @@ -0,0 +1,8 @@ +QT += testlib datavisualization + +TARGET = tst_cpptest +CONFIG += console testcase + +TEMPLATE = app + +SOURCES += tst_proxy.cpp diff --git a/tests/auto/cpptest/q3dsurface-proxy/tst_proxy.cpp b/tests/auto/cpptest/q3dsurface-proxy/tst_proxy.cpp new file mode 100644 index 00000000..f4935f96 --- /dev/null +++ b/tests/auto/cpptest/q3dsurface-proxy/tst_proxy.cpp @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +#include + +#include + +using namespace QtDataVisualization; + +class tst_proxy: public QObject +{ + Q_OBJECT + +private slots: + void initTestCase(); + void cleanupTestCase(); + void init(); + void cleanup(); + + void construct(); + + void initialProperties(); + void initializeProperties(); + void invalidProperties(); + +private: + QSurfaceDataProxy *m_proxy; +}; + +void tst_proxy::initTestCase() +{ +} + +void tst_proxy::cleanupTestCase() +{ +} + +void tst_proxy::init() +{ + m_proxy = new QSurfaceDataProxy(); +} + +void tst_proxy::cleanup() +{ + delete m_proxy; +} + +void tst_proxy::construct() +{ + QSurfaceDataProxy *proxy = new QSurfaceDataProxy(); + QVERIFY(proxy); + delete proxy; +} + +void tst_proxy::initialProperties() +{ + QVERIFY(m_proxy); +} + +void tst_proxy::initializeProperties() +{ + QVERIFY(m_proxy); +} + +void tst_proxy::invalidProperties() +{ +} + +QTEST_MAIN(tst_proxy) +#include "tst_proxy.moc" diff --git a/tests/auto/cpptest/q3dsurface-series/q3dsurface-series.pro b/tests/auto/cpptest/q3dsurface-series/q3dsurface-series.pro new file mode 100644 index 00000000..481653ef --- /dev/null +++ b/tests/auto/cpptest/q3dsurface-series/q3dsurface-series.pro @@ -0,0 +1,8 @@ +QT += testlib datavisualization + +TARGET = tst_cpptest +CONFIG += console testcase + +TEMPLATE = app + +SOURCES += tst_series.cpp diff --git a/tests/auto/cpptest/q3dsurface-series/tst_series.cpp b/tests/auto/cpptest/q3dsurface-series/tst_series.cpp new file mode 100644 index 00000000..95c3a7df --- /dev/null +++ b/tests/auto/cpptest/q3dsurface-series/tst_series.cpp @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +#include + +#include + +using namespace QtDataVisualization; + +class tst_series: public QObject +{ + Q_OBJECT + +private slots: + void initTestCase(); + void cleanupTestCase(); + void init(); + void cleanup(); + + void construct(); + + void initialProperties(); + void initializeProperties(); + void invalidProperties(); + +private: + QSurface3DSeries *m_series; +}; + +void tst_series::initTestCase() +{ +} + +void tst_series::cleanupTestCase() +{ +} + +void tst_series::init() +{ + m_series = new QSurface3DSeries(); +} + +void tst_series::cleanup() +{ + delete m_series; +} + +void tst_series::construct() +{ + QSurface3DSeries *series = new QSurface3DSeries(); + QVERIFY(series); + delete series; +} + +void tst_series::initialProperties() +{ + QVERIFY(m_series); +} + +void tst_series::initializeProperties() +{ + QVERIFY(m_series); +} + +void tst_series::invalidProperties() +{ +} + +QTEST_MAIN(tst_series) +#include "tst_series.moc" -- cgit v1.2.3 From 68fefe456b4bb99e4ac4544e1083542536e5c3ed Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Fri, 17 Oct 2014 15:55:20 +0300 Subject: Added changes file for v1.2. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: Ia89ea858a2deae2d32db81d45673692cedc95e56 Reviewed-by: Tomi Korpipää --- dist/changes-1.2.0 | 77 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 dist/changes-1.2.0 diff --git a/dist/changes-1.2.0 b/dist/changes-1.2.0 new file mode 100644 index 00000000..cd0f6ec4 --- /dev/null +++ b/dist/changes-1.2.0 @@ -0,0 +1,77 @@ +Qt Data Visualization 1.2 + +New features +------------ + +- Added support for volumetric custom objects (QCustom3DVolume) for rendering 3D voxel data. +- Reflection support for bar graphs (floor only). +- Polar horizontal axes supported for scatter and surface graphs. +- Added flipHorizontalGrid property for surface to enable displaying grid in 2D orthographic + projections of the surface (e.g. 2D spectrogram graphs). +- Added horizontalAspectRatio property for graphs to enable better control over graph dimensions. +- Added an API for setting a custom texture for a surface series. +- Added several properties to control the default input handler behavior. +- Exposed default input handlers to QML API. +- Camera can now be targeted at any point within axis ranges on the scatter and surface graphs. + On bar graphs, camera target is limited to any point on the graph floor. +- Added possibility to scale custom items based on data ranges. +- Added a property for specifying the locale for the graph, which affects how various labels + are formatted (e.g. which character is used for the decimal point). +- Added a property for specifying the Y-value of the floor level on bar graphs. +- Added a property to Q3DScene for querying the graph position at a screen position. +- The default input handlers now zoom to cursor/pinch instead of zooming toward the center of the + graph. Added a property to restore the old zoom behavior. +- Added properties to control the minimum and maximum allowable zoom level of the camera. +- Added a method for getting the list of custom items added to the graph. +- Added a property for specifying the graph background margin. + +Fixed issues +------------ + +General: +- Label widths now update consistently when axis range changes. +- Made selection texture creation more robust. +- Grid lines and labels no longer change size if aspect ratio changes. +- Q3DTheme::ColorStyleObjectGradient now works for surface graphs. +- Removed the superfluous common.pri. +- Fixed non-visible selected object drawing in static optimization mode. +- Gradient color styles are now supported equally in both default and static optimization modes. +- Specular highlight now works with rotated objects in static optimization mode. +- Fixed a crash in static optimization mode when data is updated without resizing. +- Fixed changing items in static optimization mode. +- Fixed issues with static optimization mode when some items were outside axis ranges. +- Slice mode grid lines should no longer vanish into the background when using high ambient light + value. +- Reduced the size of the surface selection texture, allowing selection to work with larger + surfaces. +- Fixed QAbstract3DGraph::renderToImage in OpenGL ES2 environments. +- Fixed crash when attempting to enable slicing without row/column selection modes. +- QCustom3DLabels now use the same shader as other labels, which means the specular highlight + no longer makes camera facing custom labels unreadable with some themes. +- Made various selection queries thread safe. +- Fixed selection query synchronization issue when using threaded renderer. +- Font size is automatically reduced if the label gets too wide to fit the label texture. +- Fixed the ordering of the subviews. +- Fixed surface normals in cases where the surface values were not in the same order + (ascending or descending) along both axes. +- Fixed a crash when removing and changing items on the same render frame. +- Fixed an issue with grid line color on surface graphs. +- Prevented selecting bars through the floor in bar graphs. +- Fixed recurring GL_INVALID_VALUE OpenGL errors. +- Improved the surface shadows. +- Fixed the OpenGL context cleanup upon renderer destruction. +- Fixed scatter item autosizing when adding a new series. +- Fixed a crash related to selection render buffer reuse. + +New examples +------------ + +- Qmlspectrogram example added. It shows how to display 2D spectrogram using surface graph with + gradients and orthographic projection. Also demonstrates the use of polar axes. +- Bars example now demonstrates zooming to selection, which leverages the new ability to + control the camera target. +- Textured surface example added. +- Volumetric example added. It shows how to use the new QCustom3DVolume object to visualize + volumetric data. +- Reflection added to Bars and Customproxy examples. +- Custom camera targeting added to Bars example. -- cgit v1.2.3 From ad6b0a468fac879b217209eb91b68e6bc143d6c4 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Mon, 20 Oct 2014 10:18:48 +0300 Subject: Document the issue with large surfaces and static scatters. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit OpenGL ES2 and/or 32bit environments can have relatively low maximum supported vertex counts per draw call, so large surfaces and static scatters may not get rendered. Task-number: QTRD-3364 Change-Id: I7fd024b0428548ac7e2ca043a06d01d2442dd226 Reviewed-by: Tomi Korpipää --- src/datavisualization/data/qsurfacedataproxy.cpp | 4 ++++ .../qtdatavisualization-qml-abstractdeclarative.qdoc | 17 +++++++++++++---- src/datavisualization/engine/qabstract3dgraph.cpp | 17 +++++++++++++---- 3 files changed, 30 insertions(+), 8 deletions(-) diff --git a/src/datavisualization/data/qsurfacedataproxy.cpp b/src/datavisualization/data/qsurfacedataproxy.cpp index dbe7cc49..12b24465 100644 --- a/src/datavisualization/data/qsurfacedataproxy.cpp +++ b/src/datavisualization/data/qsurfacedataproxy.cpp @@ -53,6 +53,10 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION * \note Surfaces with less than two rows or columns are not considered valid surfaces and will * not be rendered. * + * \note On some environments, surfaces with a lot of visible vertices may not render, because + * they exceed the per-draw vertex count supported by the graphics driver. + * This is mostly an issue on 32bit and/or OpenGL ES2 platforms. + * * \sa {Qt Data Visualization Data Handling} */ diff --git a/src/datavisualization/doc/src/qtdatavisualization-qml-abstractdeclarative.qdoc b/src/datavisualization/doc/src/qtdatavisualization-qml-abstractdeclarative.qdoc index b26f0818..b5a678e5 100644 --- a/src/datavisualization/doc/src/qtdatavisualization-qml-abstractdeclarative.qdoc +++ b/src/datavisualization/doc/src/qtdatavisualization-qml-abstractdeclarative.qdoc @@ -342,11 +342,20 @@ * \qmlproperty AbstractGraph3D.OptimizationHints AbstractGraph3D::optimizationHints * \since QtDataVisualization 1.1 * - * Defines if the rendering optimization is default or static. Default mode provides the full feature set at - * reasonable performance. Static optimizes graph rendering and is ideal for large non-changing data sets. - * It is slower with dynamic data changes and item rotations. Selection is not optimized, so using it with - * massive data sets is not advisable. Static works only on the Scatter graph. + * Defines if the rendering optimization is default or static. Default mode provides the full + * feature set at reasonable performance. Static mode optimizes graph rendering and is ideal for + * large non-changing data sets. It is slower with dynamic data changes and item rotations. + * Selection is not optimized, so using it with massive data sets is not advisable. + * Static works only on the Scatter graph. * Defaults to \c{OptimizationDefault}. + * + * \note On some environments, large graphs using static optimization may not render, because + * all of the items are rendered using a single draw call, and different graphics drivers have + * different maximum vertice counts per call that they support. + * This is mostly an issue on 32bit and/or OpenGL ES2 platforms. + * To work around this issue, choose an item mesh with low vertex count or use the point mesh. + * + * \sa Abstract3DSeries::mesh */ /*! diff --git a/src/datavisualization/engine/qabstract3dgraph.cpp b/src/datavisualization/engine/qabstract3dgraph.cpp index df6ed597..47802c89 100644 --- a/src/datavisualization/engine/qabstract3dgraph.cpp +++ b/src/datavisualization/engine/qabstract3dgraph.cpp @@ -647,11 +647,20 @@ qreal QAbstract3DGraph::aspectRatio() const /*! * \property QAbstract3DGraph::optimizationHints * - * Defines if the rendering optimization is default or static. Default mode provides the full feature set at - * reasonable performance. Static optimizes graph rendering and is ideal for large non-changing data sets. - * It is slower with dynamic data changes and item rotations. Selection is not optimized, so using it with - * massive data sets is not advisable. Static works only on the Scatter graph. + * Defines if the rendering optimization is default or static. Default mode provides the full + * feature set at reasonable performance. Static mode optimizes graph rendering and is ideal for + * large non-changing data sets. It is slower with dynamic data changes and item rotations. + * Selection is not optimized, so using it with massive data sets is not advisable. + * Static works only on the Scatter graph. * Defaults to \c{OptimizationDefault}. + * + * \note On some environments, large graphs using static optimization may not render, because + * all of the items are rendered using a single draw call, and different graphics drivers have + * different maximum vertice counts per call that they support. + * This is mostly an issue on 32bit and/or OpenGL ES2 platforms. + * To work around this issue, choose an item mesh with low vertex count or use the point mesh. + * + * \sa QAbstract3DSeries::mesh */ void QAbstract3DGraph::setOptimizationHints(OptimizationHints hints) { -- cgit v1.2.3 From ad4c012bcb1014bb82e38d26cab6374b0bda8136 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomi=20Korpip=C3=A4=C3=A4?= Date: Mon, 20 Oct 2014 11:10:00 +0300 Subject: Added C++ autotests for proxies. Task-number: QTRD-3368 Change-Id: I9412afd4c6e79835ce8ee9587736e189d4f2fbf6 Reviewed-by: Miikka Heikkinen --- .../q3dbars-modelproxy/q3dbars-modelproxy.pro | 2 +- .../auto/cpptest/q3dbars-modelproxy/tst_proxy.cpp | 128 ++++++++++++++++++- tests/auto/cpptest/q3dbars-proxy/tst_proxy.cpp | 20 ++- .../q3dscatter-modelproxy.pro | 2 +- .../cpptest/q3dscatter-modelproxy/tst_proxy.cpp | 90 +++++++++++++- tests/auto/cpptest/q3dscatter-proxy/tst_proxy.cpp | 14 ++- .../q3dsurface-heightproxy/customtexture.jpg | Bin 0 -> 516 bytes .../q3dsurface-heightproxy.pro | 3 + .../q3dsurface-heightproxy.qrc | 5 + .../cpptest/q3dsurface-heightproxy/tst_proxy.cpp | 57 +++++++++ .../q3dsurface-modelproxy.pro | 2 +- .../cpptest/q3dsurface-modelproxy/tst_proxy.cpp | 138 ++++++++++++++++++++- tests/auto/cpptest/q3dsurface-proxy/tst_proxy.cpp | 21 +++- 13 files changed, 461 insertions(+), 21 deletions(-) create mode 100644 tests/auto/cpptest/q3dsurface-heightproxy/customtexture.jpg create mode 100644 tests/auto/cpptest/q3dsurface-heightproxy/q3dsurface-heightproxy.qrc diff --git a/tests/auto/cpptest/q3dbars-modelproxy/q3dbars-modelproxy.pro b/tests/auto/cpptest/q3dbars-modelproxy/q3dbars-modelproxy.pro index b0b5d361..c383ec25 100644 --- a/tests/auto/cpptest/q3dbars-modelproxy/q3dbars-modelproxy.pro +++ b/tests/auto/cpptest/q3dbars-modelproxy/q3dbars-modelproxy.pro @@ -1,4 +1,4 @@ -QT += testlib datavisualization +QT += testlib datavisualization widgets TARGET = tst_cpptest CONFIG += console testcase diff --git a/tests/auto/cpptest/q3dbars-modelproxy/tst_proxy.cpp b/tests/auto/cpptest/q3dbars-modelproxy/tst_proxy.cpp index 9c54df53..b3661b91 100644 --- a/tests/auto/cpptest/q3dbars-modelproxy/tst_proxy.cpp +++ b/tests/auto/cpptest/q3dbars-modelproxy/tst_proxy.cpp @@ -19,6 +19,8 @@ #include #include +#include +#include using namespace QtDataVisualization; @@ -36,7 +38,8 @@ private slots: void initialProperties(); void initializeProperties(); - void invalidProperties(); + + void multiMatch(); private: QItemModelBarDataProxy *m_proxy; @@ -70,15 +73,136 @@ void tst_proxy::construct() void tst_proxy::initialProperties() { QVERIFY(m_proxy); + + QCOMPARE(m_proxy->autoColumnCategories(), true); + QCOMPARE(m_proxy->autoRowCategories(), true); + QCOMPARE(m_proxy->columnCategories(), QStringList()); + QCOMPARE(m_proxy->columnRole(), QString()); + QCOMPARE(m_proxy->columnRolePattern(), QRegExp()); + QCOMPARE(m_proxy->columnRoleReplace(), QString()); + QVERIFY(!m_proxy->itemModel()); + QCOMPARE(m_proxy->multiMatchBehavior(), QItemModelBarDataProxy::MMBLast); + QCOMPARE(m_proxy->rotationRole(), QString()); + QCOMPARE(m_proxy->rotationRolePattern(), QRegExp()); + QCOMPARE(m_proxy->rotationRoleReplace(), QString()); + QCOMPARE(m_proxy->rowCategories(), QStringList()); + QCOMPARE(m_proxy->rowRole(), QString()); + QCOMPARE(m_proxy->rowRolePattern(), QRegExp()); + QCOMPARE(m_proxy->rowRoleReplace(), QString()); + QCOMPARE(m_proxy->useModelCategories(), false); + QCOMPARE(m_proxy->valueRole(), QString()); + QCOMPARE(m_proxy->valueRolePattern(), QRegExp()); + QCOMPARE(m_proxy->valueRoleReplace(), QString()); + + QCOMPARE(m_proxy->columnLabels().count(), 0); + QCOMPARE(m_proxy->rowCount(), 0); + QCOMPARE(m_proxy->rowLabels().count(), 0); + QVERIFY(!m_proxy->series()); + + QCOMPARE(m_proxy->type(), QAbstractDataProxy::DataTypeBar); } void tst_proxy::initializeProperties() { QVERIFY(m_proxy); + + QTableWidget *table = new QTableWidget(); + + m_proxy->setAutoColumnCategories(false); + m_proxy->setAutoRowCategories(false); + m_proxy->setColumnCategories(QStringList() << "col1" << "col2"); + m_proxy->setColumnRole("column"); + m_proxy->setColumnRolePattern(QRegExp("/^.*-(\\d\\d)$/")); + m_proxy->setColumnRoleReplace("\\\\1"); + m_proxy->setItemModel(table->model()); + m_proxy->setMultiMatchBehavior(QItemModelBarDataProxy::MMBAverage); + m_proxy->setRotationRole("rotation"); + m_proxy->setRotationRolePattern(QRegExp("/-/")); + m_proxy->setRotationRoleReplace("\\\\1"); + m_proxy->setRowCategories(QStringList() << "row1" << "row2"); + m_proxy->setRowRole("row"); + m_proxy->setRowRolePattern(QRegExp("/^(\\d\\d\\d\\d).*$/")); + m_proxy->setRowRoleReplace("\\\\1"); + m_proxy->setUseModelCategories(true); + m_proxy->setValueRole("value"); + m_proxy->setValueRolePattern(QRegExp("/-/")); + m_proxy->setValueRoleReplace("\\\\1"); + + QCOMPARE(m_proxy->autoColumnCategories(), false); + QCOMPARE(m_proxy->autoRowCategories(), false); + QCOMPARE(m_proxy->columnCategories().count(), 2); + QCOMPARE(m_proxy->columnRole(), QString("column")); + QCOMPARE(m_proxy->columnRolePattern(), QRegExp("/^.*-(\\d\\d)$/")); + QCOMPARE(m_proxy->columnRoleReplace(), QString("\\\\1")); + QVERIFY(m_proxy->itemModel()); + QCOMPARE(m_proxy->multiMatchBehavior(), QItemModelBarDataProxy::MMBAverage); + QCOMPARE(m_proxy->rotationRole(), QString("rotation")); + QCOMPARE(m_proxy->rotationRolePattern(), QRegExp("/-/")); + QCOMPARE(m_proxy->rotationRoleReplace(), QString("\\\\1")); + QCOMPARE(m_proxy->rowCategories().count(), 2); + QCOMPARE(m_proxy->rowRole(), QString("row")); + QCOMPARE(m_proxy->rowRolePattern(), QRegExp("/^(\\d\\d\\d\\d).*$/")); + QCOMPARE(m_proxy->rowRoleReplace(), QString("\\\\1")); + QCOMPARE(m_proxy->useModelCategories(), true); + QCOMPARE(m_proxy->valueRole(), QString("value")); + QCOMPARE(m_proxy->valueRolePattern(), QRegExp("/-/")); + QCOMPARE(m_proxy->valueRoleReplace(), QString("\\\\1")); } -void tst_proxy::invalidProperties() +void tst_proxy::multiMatch() { + Q3DBars *graph = new Q3DBars(); + + QTableWidget *table = new QTableWidget(); + QStringList rows; + rows << "row 1" << "row 2" << "row 3"; + QStringList columns; + columns << "col 1"; + const char *values[1][3] = {{"0/0/3.5/30", "0/0/5.0/30", "0/0/6.5/30"}}; + + table->setRowCount(1); + table->setColumnCount(3); + + for (int col = 0; col < columns.size(); col++) { + for (int row = 0; row < rows.size(); row++) { + QModelIndex index = table->model()->index(col, row); + table->model()->setData(index, values[col][row]); + } + } + + m_proxy->setItemModel(table->model()); + m_proxy->setRowRole(table->model()->roleNames().value(Qt::DisplayRole)); + m_proxy->setColumnRole(table->model()->roleNames().value(Qt::DisplayRole)); + m_proxy->setRowRolePattern(QRegExp(QStringLiteral("^(\\d*)\\/(\\d*)\\/\\d*[\\.\\,]?\\d*\\/\\d*[\\.\\,]?\\d*$"))); + m_proxy->setRowRoleReplace(QStringLiteral("\\2")); + m_proxy->setValueRolePattern(QRegExp(QStringLiteral("^\\d*(\\/)(\\d*)\\/(\\d*[\\.\\,]?\\d*)\\/\\d*[\\.\\,]?\\d*$"))); + m_proxy->setValueRoleReplace(QStringLiteral("\\3")); + m_proxy->setColumnRolePattern(QRegExp(QStringLiteral("^(\\d*)(\\/)(\\d*)\\/\\d*[\\.\\,]?\\d*\\/\\d*[\\.\\,]?\\d*$"))); + m_proxy->setColumnRoleReplace(QStringLiteral("\\1")); + + QBar3DSeries *series = new QBar3DSeries(m_proxy); + + graph->addSeries(series); + + QCoreApplication::processEvents(); + QCOMPARE(graph->valueAxis()->max(), 6.5f); + m_proxy->setMultiMatchBehavior(QItemModelBarDataProxy::MMBFirst); + QCoreApplication::processEvents(); + QCOMPARE(graph->valueAxis()->max(), 3.5f); + m_proxy->setMultiMatchBehavior(QItemModelBarDataProxy::MMBLast); + QCoreApplication::processEvents(); + QCOMPARE(graph->valueAxis()->max(), 6.5f); + m_proxy->setMultiMatchBehavior(QItemModelBarDataProxy::MMBAverage); + QCoreApplication::processEvents(); + QCOMPARE(graph->valueAxis()->max(), 5.0f); + m_proxy->setMultiMatchBehavior(QItemModelBarDataProxy::MMBCumulative); + QCoreApplication::processEvents(); + QCOMPARE(graph->valueAxis()->max(), 15.0f); + + QCOMPARE(m_proxy->columnLabels().count(), 1); + QCOMPARE(m_proxy->rowCount(), 1); + QCOMPARE(m_proxy->rowLabels().count(), 1); + QVERIFY(m_proxy->series()); } QTEST_MAIN(tst_proxy) diff --git a/tests/auto/cpptest/q3dbars-proxy/tst_proxy.cpp b/tests/auto/cpptest/q3dbars-proxy/tst_proxy.cpp index d152c46b..8d64ed54 100644 --- a/tests/auto/cpptest/q3dbars-proxy/tst_proxy.cpp +++ b/tests/auto/cpptest/q3dbars-proxy/tst_proxy.cpp @@ -36,7 +36,6 @@ private slots: void initialProperties(); void initializeProperties(); - void invalidProperties(); private: QBarDataProxy *m_proxy; @@ -70,15 +69,28 @@ void tst_proxy::construct() void tst_proxy::initialProperties() { QVERIFY(m_proxy); + + QCOMPARE(m_proxy->columnLabels().count(), 0); + QCOMPARE(m_proxy->rowCount(), 0); + QCOMPARE(m_proxy->rowLabels().count(), 0); + QVERIFY(!m_proxy->series()); + + QCOMPARE(m_proxy->type(), QAbstractDataProxy::DataTypeBar); } void tst_proxy::initializeProperties() { QVERIFY(m_proxy); -} -void tst_proxy::invalidProperties() -{ + m_proxy->setColumnLabels(QStringList() << "1" << "2" << "3"); + QBarDataRow *data = new QBarDataRow; + *data << 1.0f << 3.0f << 7.5f; + m_proxy->addRow(data); + m_proxy->setRowLabels(QStringList() << "1"); + + QCOMPARE(m_proxy->columnLabels().count(), 3); + QCOMPARE(m_proxy->rowCount(), 1); + QCOMPARE(m_proxy->rowLabels().count(), 1); } QTEST_MAIN(tst_proxy) diff --git a/tests/auto/cpptest/q3dscatter-modelproxy/q3dscatter-modelproxy.pro b/tests/auto/cpptest/q3dscatter-modelproxy/q3dscatter-modelproxy.pro index b0b5d361..c383ec25 100644 --- a/tests/auto/cpptest/q3dscatter-modelproxy/q3dscatter-modelproxy.pro +++ b/tests/auto/cpptest/q3dscatter-modelproxy/q3dscatter-modelproxy.pro @@ -1,4 +1,4 @@ -QT += testlib datavisualization +QT += testlib datavisualization widgets TARGET = tst_cpptest CONFIG += console testcase diff --git a/tests/auto/cpptest/q3dscatter-modelproxy/tst_proxy.cpp b/tests/auto/cpptest/q3dscatter-modelproxy/tst_proxy.cpp index 7a4a78be..dcb0b1fb 100644 --- a/tests/auto/cpptest/q3dscatter-modelproxy/tst_proxy.cpp +++ b/tests/auto/cpptest/q3dscatter-modelproxy/tst_proxy.cpp @@ -19,6 +19,8 @@ #include #include +#include +#include using namespace QtDataVisualization; @@ -36,7 +38,8 @@ private slots: void initialProperties(); void initializeProperties(); - void invalidProperties(); + + void addModel(); private: QItemModelScatterDataProxy *m_proxy; @@ -70,15 +73,98 @@ void tst_proxy::construct() void tst_proxy::initialProperties() { QVERIFY(m_proxy); + + QVERIFY(!m_proxy->itemModel()); + QCOMPARE(m_proxy->rotationRole(), QString()); + QCOMPARE(m_proxy->rotationRolePattern(), QRegExp()); + QCOMPARE(m_proxy->rotationRoleReplace(), QString()); + QCOMPARE(m_proxy->xPosRole(), QString()); + QCOMPARE(m_proxy->xPosRolePattern(), QRegExp()); + QCOMPARE(m_proxy->xPosRoleReplace(), QString()); + QCOMPARE(m_proxy->yPosRole(), QString()); + QCOMPARE(m_proxy->yPosRolePattern(), QRegExp()); + QCOMPARE(m_proxy->yPosRoleReplace(), QString()); + QCOMPARE(m_proxy->zPosRole(), QString()); + QCOMPARE(m_proxy->zPosRolePattern(), QRegExp()); + QCOMPARE(m_proxy->zPosRoleReplace(), QString()); + + QCOMPARE(m_proxy->itemCount(), 0); + QVERIFY(!m_proxy->series()); + + QCOMPARE(m_proxy->type(), QAbstractDataProxy::DataTypeScatter); } void tst_proxy::initializeProperties() { QVERIFY(m_proxy); + + QTableWidget *table = new QTableWidget(); + + m_proxy->setItemModel(table->model()); + m_proxy->setRotationRole("rotation"); + m_proxy->setRotationRolePattern(QRegExp("/-/")); + m_proxy->setRotationRoleReplace("\\\\1"); + m_proxy->setXPosRole("X"); + m_proxy->setXPosRolePattern(QRegExp("/-/")); + m_proxy->setXPosRoleReplace("\\\\1"); + m_proxy->setYPosRole("Y"); + m_proxy->setYPosRolePattern(QRegExp("/-/")); + m_proxy->setYPosRoleReplace("\\\\1"); + m_proxy->setZPosRole("Z"); + m_proxy->setZPosRolePattern(QRegExp("/-/")); + m_proxy->setZPosRoleReplace("\\\\1"); + + QVERIFY(m_proxy->itemModel()); + QCOMPARE(m_proxy->rotationRole(), QString("rotation")); + QCOMPARE(m_proxy->rotationRolePattern(), QRegExp("/-/")); + QCOMPARE(m_proxy->rotationRoleReplace(), QString("\\\\1")); + QCOMPARE(m_proxy->xPosRole(), QString("X")); + QCOMPARE(m_proxy->xPosRolePattern(), QRegExp("/-/")); + QCOMPARE(m_proxy->xPosRoleReplace(), QString("\\\\1")); + QCOMPARE(m_proxy->yPosRole(), QString("Y")); + QCOMPARE(m_proxy->yPosRolePattern(), QRegExp("/-/")); + QCOMPARE(m_proxy->yPosRoleReplace(), QString("\\\\1")); + QCOMPARE(m_proxy->zPosRole(), QString("Z")); + QCOMPARE(m_proxy->zPosRolePattern(), QRegExp("/-/")); + QCOMPARE(m_proxy->zPosRoleReplace(), QString("\\\\1")); } -void tst_proxy::invalidProperties() +void tst_proxy::addModel() { + QTableWidget *table = new QTableWidget(); + QStringList rows; + rows << "row 1"; + QStringList columns; + columns << "col 1"; + const char *values[1][2] = {{"0/0/5.5/30", "0/0/10.5/30"}}; + + table->setRowCount(2); + table->setColumnCount(1); + + for (int col = 0; col < columns.size(); col++) { + for (int row = 0; row < rows.size(); row++) { + QModelIndex index = table->model()->index(col, row); + table->model()->setData(index, values[col][row]); + } + } + + m_proxy->setItemModel(table->model()); + m_proxy->setXPosRole(table->model()->roleNames().value(Qt::DisplayRole)); + m_proxy->setZPosRole(table->model()->roleNames().value(Qt::DisplayRole)); + m_proxy->setXPosRolePattern(QRegExp(QStringLiteral("^(\\d*)\\/(\\d*)\\/\\d*[\\.\\,]?\\d*\\/\\d*[\\.\\,]?\\d*$"))); + m_proxy->setXPosRoleReplace(QStringLiteral("\\2")); + m_proxy->setYPosRolePattern(QRegExp(QStringLiteral("^\\d*(\\/)(\\d*)\\/(\\d*[\\.\\,]?\\d*)\\/\\d*[\\.\\,]?\\d*$"))); + m_proxy->setYPosRoleReplace(QStringLiteral("\\3")); + m_proxy->setZPosRolePattern(QRegExp(QStringLiteral("^(\\d*)(\\/)(\\d*)\\/\\d*[\\.\\,]?\\d*\\/\\d*[\\.\\,]?\\d*$"))); + m_proxy->setZPosRoleReplace(QStringLiteral("\\1")); + + QScatter3DSeries *series = new QScatter3DSeries(m_proxy); + Q_UNUSED(series) + + QCoreApplication::processEvents(); + + QCOMPARE(m_proxy->itemCount(), 2); + QVERIFY(m_proxy->series()); } QTEST_MAIN(tst_proxy) diff --git a/tests/auto/cpptest/q3dscatter-proxy/tst_proxy.cpp b/tests/auto/cpptest/q3dscatter-proxy/tst_proxy.cpp index 00bbadb4..436350dc 100644 --- a/tests/auto/cpptest/q3dscatter-proxy/tst_proxy.cpp +++ b/tests/auto/cpptest/q3dscatter-proxy/tst_proxy.cpp @@ -36,7 +36,6 @@ private slots: void initialProperties(); void initializeProperties(); - void invalidProperties(); private: QScatterDataProxy *m_proxy; @@ -70,15 +69,22 @@ void tst_proxy::construct() void tst_proxy::initialProperties() { QVERIFY(m_proxy); + + QCOMPARE(m_proxy->itemCount(), 0); + QVERIFY(!m_proxy->series()); + + QCOMPARE(m_proxy->type(), QAbstractDataProxy::DataTypeScatter); } void tst_proxy::initializeProperties() { QVERIFY(m_proxy); -} -void tst_proxy::invalidProperties() -{ + QScatterDataArray data; + data << QVector3D(0.5f, 0.5f, 0.5f) << QVector3D(-0.3f, -0.5f, -0.4f); + m_proxy->addItems(data); + + QCOMPARE(m_proxy->itemCount(), 2); } QTEST_MAIN(tst_proxy) diff --git a/tests/auto/cpptest/q3dsurface-heightproxy/customtexture.jpg b/tests/auto/cpptest/q3dsurface-heightproxy/customtexture.jpg new file mode 100644 index 00000000..2580f5bd Binary files /dev/null and b/tests/auto/cpptest/q3dsurface-heightproxy/customtexture.jpg differ diff --git a/tests/auto/cpptest/q3dsurface-heightproxy/q3dsurface-heightproxy.pro b/tests/auto/cpptest/q3dsurface-heightproxy/q3dsurface-heightproxy.pro index b0b5d361..56a964d0 100644 --- a/tests/auto/cpptest/q3dsurface-heightproxy/q3dsurface-heightproxy.pro +++ b/tests/auto/cpptest/q3dsurface-heightproxy/q3dsurface-heightproxy.pro @@ -6,3 +6,6 @@ CONFIG += console testcase TEMPLATE = app SOURCES += tst_proxy.cpp + +RESOURCES += \ + q3dsurface-heightproxy.qrc diff --git a/tests/auto/cpptest/q3dsurface-heightproxy/q3dsurface-heightproxy.qrc b/tests/auto/cpptest/q3dsurface-heightproxy/q3dsurface-heightproxy.qrc new file mode 100644 index 00000000..b83c7ef9 --- /dev/null +++ b/tests/auto/cpptest/q3dsurface-heightproxy/q3dsurface-heightproxy.qrc @@ -0,0 +1,5 @@ + + + customtexture.jpg + + diff --git a/tests/auto/cpptest/q3dsurface-heightproxy/tst_proxy.cpp b/tests/auto/cpptest/q3dsurface-heightproxy/tst_proxy.cpp index cf896bbf..8e998568 100644 --- a/tests/auto/cpptest/q3dsurface-heightproxy/tst_proxy.cpp +++ b/tests/auto/cpptest/q3dsurface-heightproxy/tst_proxy.cpp @@ -70,15 +70,72 @@ void tst_proxy::construct() void tst_proxy::initialProperties() { QVERIFY(m_proxy); + + QCOMPARE(m_proxy->heightMap(), QImage()); + QCOMPARE(m_proxy->heightMapFile(), QString("")); + QCOMPARE(m_proxy->maxXValue(), 10.0f); + QCOMPARE(m_proxy->maxZValue(), 10.0f); + QCOMPARE(m_proxy->minXValue(), 0.0f); + QCOMPARE(m_proxy->minZValue(), 0.0f); + + QCOMPARE(m_proxy->columnCount(), 0); + QCOMPARE(m_proxy->rowCount(), 0); + QVERIFY(!m_proxy->series()); + + QCOMPARE(m_proxy->type(), QAbstractDataProxy::DataTypeSurface); } void tst_proxy::initializeProperties() { QVERIFY(m_proxy); + + m_proxy->setHeightMapFile(":/customtexture.jpg"); + m_proxy->setMaxXValue(11.0f); + m_proxy->setMaxZValue(11.0f); + m_proxy->setMinXValue(-10.0f); + m_proxy->setMinZValue(-10.0f); + + QCoreApplication::processEvents(); + + QCOMPARE(m_proxy->heightMapFile(), QString(":/customtexture.jpg")); + QCOMPARE(m_proxy->maxXValue(), 11.0f); + QCOMPARE(m_proxy->maxZValue(), 11.0f); + QCOMPARE(m_proxy->minXValue(), -10.0f); + QCOMPARE(m_proxy->minZValue(), -10.0f); + + QCOMPARE(m_proxy->columnCount(), 24); + QCOMPARE(m_proxy->rowCount(), 24); + + m_proxy->setHeightMapFile(""); + + QCoreApplication::processEvents(); + + QCOMPARE(m_proxy->columnCount(), 0); + QCOMPARE(m_proxy->rowCount(), 0); + + m_proxy->setHeightMap(QImage(":/customtexture.jpg")); + + QCoreApplication::processEvents(); + + QCOMPARE(m_proxy->columnCount(), 24); + QCOMPARE(m_proxy->rowCount(), 24); } void tst_proxy::invalidProperties() { + m_proxy->setMaxXValue(-10.0f); + m_proxy->setMaxZValue(-10.0f); + QCOMPARE(m_proxy->maxXValue(), -10.0f); + QCOMPARE(m_proxy->maxZValue(), -10.0f); + QCOMPARE(m_proxy->minXValue(), -11.0f); + QCOMPARE(m_proxy->minZValue(), -11.0f); + + m_proxy->setMinXValue(10.0f); + m_proxy->setMinZValue(10.0f); + QCOMPARE(m_proxy->maxXValue(), 11.0f); + QCOMPARE(m_proxy->maxZValue(), 11.0f); + QCOMPARE(m_proxy->minXValue(), 10.0f); + QCOMPARE(m_proxy->minZValue(), 10.0f); } QTEST_MAIN(tst_proxy) diff --git a/tests/auto/cpptest/q3dsurface-modelproxy/q3dsurface-modelproxy.pro b/tests/auto/cpptest/q3dsurface-modelproxy/q3dsurface-modelproxy.pro index b0b5d361..c383ec25 100644 --- a/tests/auto/cpptest/q3dsurface-modelproxy/q3dsurface-modelproxy.pro +++ b/tests/auto/cpptest/q3dsurface-modelproxy/q3dsurface-modelproxy.pro @@ -1,4 +1,4 @@ -QT += testlib datavisualization +QT += testlib datavisualization widgets TARGET = tst_cpptest CONFIG += console testcase diff --git a/tests/auto/cpptest/q3dsurface-modelproxy/tst_proxy.cpp b/tests/auto/cpptest/q3dsurface-modelproxy/tst_proxy.cpp index c7c1dd83..f7311ca1 100644 --- a/tests/auto/cpptest/q3dsurface-modelproxy/tst_proxy.cpp +++ b/tests/auto/cpptest/q3dsurface-modelproxy/tst_proxy.cpp @@ -19,6 +19,8 @@ #include #include +#include +#include using namespace QtDataVisualization; @@ -36,7 +38,8 @@ private slots: void initialProperties(); void initializeProperties(); - void invalidProperties(); + + void multiMatch(); private: QItemModelSurfaceDataProxy *m_proxy; @@ -70,15 +73,146 @@ void tst_proxy::construct() void tst_proxy::initialProperties() { QVERIFY(m_proxy); + + QCOMPARE(m_proxy->autoColumnCategories(), true); + QCOMPARE(m_proxy->autoRowCategories(), true); + QCOMPARE(m_proxy->columnCategories(), QStringList()); + QCOMPARE(m_proxy->columnRole(), QString()); + QCOMPARE(m_proxy->columnRolePattern(), QRegExp()); + QCOMPARE(m_proxy->columnRoleReplace(), QString()); + QVERIFY(!m_proxy->itemModel()); + QCOMPARE(m_proxy->multiMatchBehavior(), QItemModelSurfaceDataProxy::MMBLast); + QCOMPARE(m_proxy->rowCategories(), QStringList()); + QCOMPARE(m_proxy->rowRole(), QString()); + QCOMPARE(m_proxy->rowRolePattern(), QRegExp()); + QCOMPARE(m_proxy->rowRoleReplace(), QString()); + QCOMPARE(m_proxy->useModelCategories(), false); + QCOMPARE(m_proxy->xPosRole(), QString()); + QCOMPARE(m_proxy->xPosRolePattern(), QRegExp()); + QCOMPARE(m_proxy->xPosRoleReplace(), QString()); + QCOMPARE(m_proxy->yPosRole(), QString()); + QCOMPARE(m_proxy->yPosRolePattern(), QRegExp()); + QCOMPARE(m_proxy->yPosRoleReplace(), QString()); + QCOMPARE(m_proxy->zPosRole(), QString()); + QCOMPARE(m_proxy->zPosRolePattern(), QRegExp()); + QCOMPARE(m_proxy->zPosRoleReplace(), QString()); + + QCOMPARE(m_proxy->columnCount(), 0); + QCOMPARE(m_proxy->rowCount(), 0); + QVERIFY(!m_proxy->series()); + + QCOMPARE(m_proxy->type(), QAbstractDataProxy::DataTypeSurface); } void tst_proxy::initializeProperties() { QVERIFY(m_proxy); + + QTableWidget *table = new QTableWidget(); + + m_proxy->setAutoColumnCategories(false); + m_proxy->setAutoRowCategories(false); + m_proxy->setColumnCategories(QStringList() << "col1" << "col2"); + m_proxy->setColumnRole("column"); + m_proxy->setColumnRolePattern(QRegExp("/^.*-(\\d\\d)$/")); + m_proxy->setColumnRoleReplace("\\\\1"); + m_proxy->setItemModel(table->model()); + m_proxy->setMultiMatchBehavior(QItemModelSurfaceDataProxy::MMBAverage); + m_proxy->setRowCategories(QStringList() << "row1" << "row2"); + m_proxy->setRowRole("row"); + m_proxy->setRowRolePattern(QRegExp("/^(\\d\\d\\d\\d).*$/")); + m_proxy->setRowRoleReplace("\\\\1"); + m_proxy->setUseModelCategories(true); + m_proxy->setXPosRole("X"); + m_proxy->setXPosRolePattern(QRegExp("/-/")); + m_proxy->setXPosRoleReplace("\\\\1"); + m_proxy->setYPosRole("Y"); + m_proxy->setYPosRolePattern(QRegExp("/-/")); + m_proxy->setYPosRoleReplace("\\\\1"); + m_proxy->setZPosRole("Z"); + m_proxy->setZPosRolePattern(QRegExp("/-/")); + m_proxy->setZPosRoleReplace("\\\\1"); + + QCOMPARE(m_proxy->autoColumnCategories(), false); + QCOMPARE(m_proxy->autoRowCategories(), false); + QCOMPARE(m_proxy->columnCategories().count(), 2); + QCOMPARE(m_proxy->columnRole(), QString("column")); + QCOMPARE(m_proxy->columnRolePattern(), QRegExp("/^.*-(\\d\\d)$/")); + QCOMPARE(m_proxy->columnRoleReplace(), QString("\\\\1")); + QVERIFY(m_proxy->itemModel()); + QCOMPARE(m_proxy->multiMatchBehavior(), QItemModelSurfaceDataProxy::MMBAverage); + QCOMPARE(m_proxy->rowCategories().count(), 2); + QCOMPARE(m_proxy->rowRole(), QString("row")); + QCOMPARE(m_proxy->rowRolePattern(), QRegExp("/^(\\d\\d\\d\\d).*$/")); + QCOMPARE(m_proxy->rowRoleReplace(), QString("\\\\1")); + QCOMPARE(m_proxy->useModelCategories(), true); + QCOMPARE(m_proxy->xPosRole(), QString("X")); + QCOMPARE(m_proxy->xPosRolePattern(), QRegExp("/-/")); + QCOMPARE(m_proxy->xPosRoleReplace(), QString("\\\\1")); + QCOMPARE(m_proxy->yPosRole(), QString("Y")); + QCOMPARE(m_proxy->yPosRolePattern(), QRegExp("/-/")); + QCOMPARE(m_proxy->yPosRoleReplace(), QString("\\\\1")); + QCOMPARE(m_proxy->zPosRole(), QString("Z")); + QCOMPARE(m_proxy->zPosRolePattern(), QRegExp("/-/")); + QCOMPARE(m_proxy->zPosRoleReplace(), QString("\\\\1")); } -void tst_proxy::invalidProperties() +void tst_proxy::multiMatch() { + Q3DSurface *graph = new Q3DSurface(); + + QTableWidget *table = new QTableWidget(); + QStringList rows; + rows << "row 1" << "row 2"; + QStringList columns; + columns << "col 1" << "col 2" << "col 3" << "col 4"; + const char *values[4][2] = {{"0/0/5.5/30", "0/0/10.5/30"}, + {"0/1/5.5/30", "0/1/0.5/30"}, + {"1/0/5.5/30", "1/0/0.5/30"}, + {"1/1/0.0/30", "1/1/0.0/30"}}; + + table->setRowCount(2); + table->setColumnCount(4); + + for (int col = 0; col < columns.size(); col++) { + for (int row = 0; row < rows.size(); row++) { + QModelIndex index = table->model()->index(col, row); + table->model()->setData(index, values[col][row]); + } + } + + m_proxy->setItemModel(table->model()); + m_proxy->setRowRole(table->model()->roleNames().value(Qt::DisplayRole)); + m_proxy->setColumnRole(table->model()->roleNames().value(Qt::DisplayRole)); + m_proxy->setRowRolePattern(QRegExp(QStringLiteral("^(\\d*)\\/(\\d*)\\/\\d*[\\.\\,]?\\d*\\/\\d*[\\.\\,]?\\d*$"))); + m_proxy->setRowRoleReplace(QStringLiteral("\\2")); + m_proxy->setYPosRolePattern(QRegExp(QStringLiteral("^\\d*(\\/)(\\d*)\\/(\\d*[\\.\\,]?\\d*)\\/\\d*[\\.\\,]?\\d*$"))); + m_proxy->setYPosRoleReplace(QStringLiteral("\\3")); + m_proxy->setColumnRolePattern(QRegExp(QStringLiteral("^(\\d*)(\\/)(\\d*)\\/\\d*[\\.\\,]?\\d*\\/\\d*[\\.\\,]?\\d*$"))); + m_proxy->setColumnRoleReplace(QStringLiteral("\\1")); + + QSurface3DSeries *series = new QSurface3DSeries(m_proxy); + + graph->addSeries(series); + + QCoreApplication::processEvents(); + QCOMPARE(graph->axisY()->max(), 10.5f); + m_proxy->setMultiMatchBehavior(QItemModelSurfaceDataProxy::MMBFirst); + QCoreApplication::processEvents(); + QCOMPARE(graph->axisY()->max(), 5.5f); + m_proxy->setMultiMatchBehavior(QItemModelSurfaceDataProxy::MMBLast); + QCoreApplication::processEvents(); + QCOMPARE(graph->axisY()->max(), 10.5f); + m_proxy->setMultiMatchBehavior(QItemModelSurfaceDataProxy::MMBAverage); + QCoreApplication::processEvents(); + QCOMPARE(graph->axisY()->max(), 8.0f); + m_proxy->setMultiMatchBehavior(QItemModelSurfaceDataProxy::MMBCumulativeY); + QCoreApplication::processEvents(); + QCOMPARE(graph->axisY()->max(), 16.0f); + + QCOMPARE(m_proxy->columnCount(), 2); + QCOMPARE(m_proxy->rowCount(), 3); + QVERIFY(m_proxy->series()); } QTEST_MAIN(tst_proxy) diff --git a/tests/auto/cpptest/q3dsurface-proxy/tst_proxy.cpp b/tests/auto/cpptest/q3dsurface-proxy/tst_proxy.cpp index f4935f96..4274899d 100644 --- a/tests/auto/cpptest/q3dsurface-proxy/tst_proxy.cpp +++ b/tests/auto/cpptest/q3dsurface-proxy/tst_proxy.cpp @@ -36,7 +36,6 @@ private slots: void initialProperties(); void initializeProperties(); - void invalidProperties(); private: QSurfaceDataProxy *m_proxy; @@ -70,15 +69,29 @@ void tst_proxy::construct() void tst_proxy::initialProperties() { QVERIFY(m_proxy); + + QCOMPARE(m_proxy->columnCount(), 0); + QCOMPARE(m_proxy->rowCount(), 0); + QVERIFY(!m_proxy->series()); + + QCOMPARE(m_proxy->type(), QAbstractDataProxy::DataTypeSurface); } void tst_proxy::initializeProperties() { QVERIFY(m_proxy); -} -void tst_proxy::invalidProperties() -{ + QSurfaceDataArray *data = new QSurfaceDataArray; + QSurfaceDataRow *dataRow1 = new QSurfaceDataRow; + QSurfaceDataRow *dataRow2 = new QSurfaceDataRow; + *dataRow1 << QVector3D(0.0f, 0.1f, 0.5f) << QVector3D(1.0f, 0.5f, 0.5f); + *dataRow2 << QVector3D(0.0f, 1.8f, 1.0f) << QVector3D(1.0f, 1.2f, 1.0f); + *data << dataRow1 << dataRow2; + + m_proxy->resetArray(data); + + QCOMPARE(m_proxy->columnCount(), 2); + QCOMPARE(m_proxy->rowCount(), 2); } QTEST_MAIN(tst_proxy) -- cgit v1.2.3 From 0cc8662c1b245c50910f44153fdb3124dd22abd8 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Mon, 20 Oct 2014 15:51:49 +0300 Subject: Fix shadows when viewing directly from above. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fudge the camera y-angle so that it's never looking directly from above or below. The actual problem is that eye vector is paraller to up vector if viewing directly from above, which results in invalid view matrix. Task-number: QTRD-2789 Change-Id: Ia79b8bb4b8317c6aaf6ac4c70a6bb70c24b5309e Reviewed-by: Tomi Korpipää --- src/datavisualization/engine/q3dcamera.cpp | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/src/datavisualization/engine/q3dcamera.cpp b/src/datavisualization/engine/q3dcamera.cpp index 273b8415..897c3779 100644 --- a/src/datavisualization/engine/q3dcamera.cpp +++ b/src/datavisualization/engine/q3dcamera.cpp @@ -885,20 +885,33 @@ QVector3D Q3DCameraPrivate::calculatePositionRelativeToCamera(const QVector3D &r float distanceModifier) const { // Move the position with camera - GLfloat radiusFactor = cameraDistance * (1.5f + distanceModifier); - GLfloat xAngle; - GLfloat yAngle; + const float radiusFactor = cameraDistance * (1.5f + distanceModifier); + float xAngle; + float yAngle; + if (!fixedRotation) { xAngle = qDegreesToRadians(m_xRotation); - yAngle = qDegreesToRadians(m_yRotation); + float yRotation = m_yRotation; + // Light must not be paraller to eye vector, so fudge the y rotation a bit. + // Note: This needs redoing if we ever allow arbitrary light positioning. + const float yMargin = 0.1f; // Smaller margins cause weird shadow artifacts on tops of bars + const float absYRotation = qAbs(yRotation); + if (absYRotation < 90.0f + yMargin && absYRotation > 90.0f - yMargin) { + if (yRotation < 0.0f) + yRotation = -90.0f + yMargin; + else + yRotation = 90.0f - yMargin; + } + yAngle = qDegreesToRadians(yRotation); } else { xAngle = qDegreesToRadians(fixedRotation); yAngle = 0; } - GLfloat radius = (radiusFactor + relativePosition.y()); // set radius to match the highest height of the position - GLfloat zPos = radius * qCos(xAngle) * qCos(yAngle); - GLfloat xPos = radius * qSin(xAngle) * qCos(yAngle); - GLfloat yPos = (radiusFactor + relativePosition.y()) * qSin(yAngle); + // Set radius to match the highest height of the position + const float radius = (radiusFactor + relativePosition.y()); + const float zPos = radius * qCos(xAngle) * qCos(yAngle); + const float xPos = radius * qSin(xAngle) * qCos(yAngle); + const float yPos = radius * qSin(yAngle); // Keep in the set position in relation to camera return QVector3D(-xPos + relativePosition.x(), -- cgit v1.2.3 From f7f1e1373e652ea2de1666f4b626bf35e367d84b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomi=20Korpip=C3=A4=C3=A4?= Date: Tue, 21 Oct 2014 11:05:45 +0300 Subject: Added C++ autotests for series Also fixed a bug found in testing, and added tests for optional constructors for already tested classes. Task-number: QTRD-3368 Change-Id: I2214f28e2c5069ecab422fc6817acb2f0c0b192b Reviewed-by: Miikka Heikkinen --- src/datavisualization/data/qabstract3dseries.cpp | 3 + .../auto/cpptest/q3dbars-modelproxy/tst_proxy.cpp | 58 +++++++++++++++ tests/auto/cpptest/q3dbars-series/tst_series.cpp | 84 ++++++++++++++++++++++ tests/auto/cpptest/q3dbars/tst_bars.cpp | 4 ++ .../cpptest/q3dscatter-modelproxy/tst_proxy.cpp | 22 ++++++ .../auto/cpptest/q3dscatter-series/tst_series.cpp | 33 +++++++-- tests/auto/cpptest/q3dscatter/tst_scatter.cpp | 4 ++ .../cpptest/q3dsurface-heightproxy/tst_proxy.cpp | 14 ++++ .../cpptest/q3dsurface-modelproxy/tst_proxy.cpp | 64 +++++++++++++++++ .../auto/cpptest/q3dsurface-series/tst_series.cpp | 35 +++++++++ tests/auto/cpptest/q3dsurface/tst_surface.cpp | 4 ++ 11 files changed, 321 insertions(+), 4 deletions(-) diff --git a/src/datavisualization/data/qabstract3dseries.cpp b/src/datavisualization/data/qabstract3dseries.cpp index f8fe6b4f..caa95062 100644 --- a/src/datavisualization/data/qabstract3dseries.cpp +++ b/src/datavisualization/data/qabstract3dseries.cpp @@ -657,6 +657,9 @@ QAbstract3DSeriesPrivate::QAbstract3DSeriesPrivate(QAbstract3DSeries *q, m_mesh(QAbstract3DSeries::MeshCube), m_meshSmooth(false), m_colorStyle(Q3DTheme::ColorStyleUniform), + m_baseColor(Qt::black), + m_singleHighlightColor(Qt::black), + m_multiHighlightColor(Qt::black), m_itemLabelDirty(true), m_itemLabelVisible(true) { diff --git a/tests/auto/cpptest/q3dbars-modelproxy/tst_proxy.cpp b/tests/auto/cpptest/q3dbars-modelproxy/tst_proxy.cpp index b3661b91..c65e151b 100644 --- a/tests/auto/cpptest/q3dbars-modelproxy/tst_proxy.cpp +++ b/tests/auto/cpptest/q3dbars-modelproxy/tst_proxy.cpp @@ -68,6 +68,64 @@ void tst_proxy::construct() QItemModelBarDataProxy *proxy = new QItemModelBarDataProxy(); QVERIFY(proxy); delete proxy; + + QTableWidget *table = new QTableWidget(); + + proxy = new QItemModelBarDataProxy(table->model()); + QVERIFY(proxy); + delete proxy; + + proxy = new QItemModelBarDataProxy(table->model(), "val"); + QVERIFY(proxy); + QCOMPARE(proxy->rowRole(), QString("")); + QCOMPARE(proxy->columnRole(), QString("")); + QCOMPARE(proxy->valueRole(), QString("val")); + QCOMPARE(proxy->rotationRole(), QString("")); + QCOMPARE(proxy->rowCategories().length(), 0); + QCOMPARE(proxy->columnCategories().length(), 0); + delete proxy; + + proxy = new QItemModelBarDataProxy(table->model(), "row", "col", "val"); + QVERIFY(proxy); + QCOMPARE(proxy->rowRole(), QString("row")); + QCOMPARE(proxy->columnRole(), QString("col")); + QCOMPARE(proxy->valueRole(), QString("val")); + QCOMPARE(proxy->rotationRole(), QString("")); + QCOMPARE(proxy->rowCategories().length(), 0); + QCOMPARE(proxy->columnCategories().length(), 0); + delete proxy; + + proxy = new QItemModelBarDataProxy(table->model(), "row", "col", "val", "rot"); + QVERIFY(proxy); + QCOMPARE(proxy->rowRole(), QString("row")); + QCOMPARE(proxy->columnRole(), QString("col")); + QCOMPARE(proxy->valueRole(), QString("val")); + QCOMPARE(proxy->rotationRole(), QString("rot")); + QCOMPARE(proxy->rowCategories().length(), 0); + QCOMPARE(proxy->columnCategories().length(), 0); + delete proxy; + + proxy = new QItemModelBarDataProxy(table->model(), "row", "col", "val", + QStringList() << "rowCat", QStringList() << "colCat"); + QVERIFY(proxy); + QCOMPARE(proxy->rowRole(), QString("row")); + QCOMPARE(proxy->columnRole(), QString("col")); + QCOMPARE(proxy->valueRole(), QString("val")); + QCOMPARE(proxy->rotationRole(), QString("")); + QCOMPARE(proxy->rowCategories().length(), 1); + QCOMPARE(proxy->columnCategories().length(), 1); + delete proxy; + + proxy = new QItemModelBarDataProxy(table->model(), "row", "col", "val", "rot", + QStringList() << "rowCat", QStringList() << "colCat"); + QVERIFY(proxy); + QCOMPARE(proxy->rowRole(), QString("row")); + QCOMPARE(proxy->columnRole(), QString("col")); + QCOMPARE(proxy->valueRole(), QString("val")); + QCOMPARE(proxy->rotationRole(), QString("rot")); + QCOMPARE(proxy->rowCategories().length(), 1); + QCOMPARE(proxy->columnCategories().length(), 1); + delete proxy; } void tst_proxy::initialProperties() diff --git a/tests/auto/cpptest/q3dbars-series/tst_series.cpp b/tests/auto/cpptest/q3dbars-series/tst_series.cpp index 706baf94..e2a40ae7 100644 --- a/tests/auto/cpptest/q3dbars-series/tst_series.cpp +++ b/tests/auto/cpptest/q3dbars-series/tst_series.cpp @@ -65,20 +65,104 @@ void tst_series::construct() QBar3DSeries *series = new QBar3DSeries(); QVERIFY(series); delete series; + + QBarDataProxy *proxy = new QBarDataProxy(); + + series = new QBar3DSeries(proxy); + QVERIFY(series); + QCOMPARE(series->dataProxy(), proxy); + delete series; } void tst_series::initialProperties() { QVERIFY(m_series); + + QVERIFY(m_series->dataProxy()); + QCOMPARE(m_series->meshAngle(), 0.0f); + QCOMPARE(m_series->selectedBar(), m_series->invalidSelectionPosition()); + + // Common properties + QCOMPARE(m_series->baseColor(), QColor(Qt::black)); + QCOMPARE(m_series->baseGradient(), QLinearGradient()); + QCOMPARE(m_series->colorStyle(), Q3DTheme::ColorStyleUniform); + QCOMPARE(m_series->itemLabel(), QString("")); + QCOMPARE(m_series->itemLabelFormat(), QString("@valueLabel")); + QCOMPARE(m_series->isItemLabelVisible(), true); + QCOMPARE(m_series->mesh(), QAbstract3DSeries::MeshBevelBar); + QCOMPARE(m_series->meshRotation(), QQuaternion(1, 0, 0, 0)); + QCOMPARE(m_series->isMeshSmooth(), false); + QCOMPARE(m_series->multiHighlightColor(), QColor(Qt::black)); + QCOMPARE(m_series->multiHighlightGradient(), QLinearGradient()); + QCOMPARE(m_series->name(), QString("")); + QCOMPARE(m_series->singleHighlightColor(), QColor(Qt::black)); + QCOMPARE(m_series->singleHighlightGradient(), QLinearGradient()); + QCOMPARE(m_series->type(), QAbstract3DSeries::SeriesTypeBar); + QCOMPARE(m_series->userDefinedMesh(), QString("")); + QCOMPARE(m_series->isVisible(), true); } void tst_series::initializeProperties() { QVERIFY(m_series); + + m_series->setDataProxy(new QBarDataProxy()); + m_series->setMeshAngle(15.0f); + m_series->setSelectedBar(QPoint(0, 0)); + + QCOMPARE(m_series->meshAngle(), 15.0f); + QCOMPARE(m_series->selectedBar(), QPoint(0, 0)); + + QLinearGradient gradient1; + gradient1.setColorAt(0.0, Qt::red); + gradient1.setColorAt(1.0, Qt::blue); + QLinearGradient gradient2; + gradient2.setColorAt(0.0, Qt::yellow); + gradient2.setColorAt(1.0, Qt::green); + QLinearGradient gradient3; + gradient3.setColorAt(0.0, Qt::white); + gradient3.setColorAt(1.0, Qt::gray); + + // Common properties + m_series->setBaseColor(QColor(Qt::blue)); + m_series->setBaseGradient(gradient1); + m_series->setColorStyle(Q3DTheme::ColorStyleRangeGradient); + m_series->setItemLabelFormat("%f"); + m_series->setItemLabelVisible(false); + m_series->setMesh(QAbstract3DSeries::MeshCone); + m_series->setMeshSmooth(true); + m_series->setMultiHighlightColor(QColor(Qt::green)); + m_series->setMultiHighlightGradient(gradient2); + m_series->setName("name"); + m_series->setSingleHighlightColor(QColor(Qt::red)); + m_series->setSingleHighlightGradient(gradient3); + m_series->setUserDefinedMesh(":/customitem.obj"); + m_series->setVisible(false); + + QCOMPARE(m_series->baseColor(), QColor(Qt::blue)); + QCOMPARE(m_series->baseGradient(), gradient1); + QCOMPARE(m_series->baseGradient().stops().at(0).second, QColor(Qt::red)); + QCOMPARE(m_series->colorStyle(), Q3DTheme::ColorStyleRangeGradient); + QCOMPARE(m_series->itemLabelFormat(), QString("%f")); + QCOMPARE(m_series->isItemLabelVisible(), false); + QCOMPARE(m_series->mesh(), QAbstract3DSeries::MeshCone); + QCOMPARE(m_series->isMeshSmooth(), true); + QCOMPARE(m_series->multiHighlightColor(), QColor(Qt::green)); + QCOMPARE(m_series->multiHighlightGradient(), gradient2); + QCOMPARE(m_series->multiHighlightGradient().stops().at(0).second, QColor(Qt::yellow)); + QCOMPARE(m_series->name(), QString("name")); + QCOMPARE(m_series->singleHighlightColor(), QColor(Qt::red)); + QCOMPARE(m_series->singleHighlightGradient(), gradient3); + QCOMPARE(m_series->singleHighlightGradient().stops().at(0).second, QColor(Qt::white)); + QCOMPARE(m_series->userDefinedMesh(), QString(":/customitem.obj")); + QCOMPARE(m_series->isVisible(), false); } void tst_series::invalidProperties() { + m_series->setMesh(QAbstract3DSeries::MeshMinimal); + + QCOMPARE(m_series->mesh(), QAbstract3DSeries::MeshBevelBar); } QTEST_MAIN(tst_series) diff --git a/tests/auto/cpptest/q3dbars/tst_bars.cpp b/tests/auto/cpptest/q3dbars/tst_bars.cpp index 630afac3..4535c026 100644 --- a/tests/auto/cpptest/q3dbars/tst_bars.cpp +++ b/tests/auto/cpptest/q3dbars/tst_bars.cpp @@ -95,6 +95,10 @@ void tst_bars::construct() Q3DBars *graph = new Q3DBars(); QVERIFY(graph); delete graph; + + graph = new Q3DBars(new QSurfaceFormat()); + QVERIFY(graph); + delete graph; } void tst_bars::initialProperties() diff --git a/tests/auto/cpptest/q3dscatter-modelproxy/tst_proxy.cpp b/tests/auto/cpptest/q3dscatter-modelproxy/tst_proxy.cpp index dcb0b1fb..9d5cea90 100644 --- a/tests/auto/cpptest/q3dscatter-modelproxy/tst_proxy.cpp +++ b/tests/auto/cpptest/q3dscatter-modelproxy/tst_proxy.cpp @@ -68,6 +68,28 @@ void tst_proxy::construct() QItemModelScatterDataProxy *proxy = new QItemModelScatterDataProxy(); QVERIFY(proxy); delete proxy; + + QTableWidget *table = new QTableWidget(); + + proxy = new QItemModelScatterDataProxy(table->model()); + QVERIFY(proxy); + delete proxy; + + proxy = new QItemModelScatterDataProxy(table->model(), "x", "y", "z"); + QVERIFY(proxy); + QCOMPARE(proxy->xPosRole(), QString("x")); + QCOMPARE(proxy->yPosRole(), QString("y")); + QCOMPARE(proxy->zPosRole(), QString("z")); + QCOMPARE(proxy->rotationRole(), QString("")); + delete proxy; + + proxy = new QItemModelScatterDataProxy(table->model(), "x", "y", "z", "rot"); + QVERIFY(proxy); + QCOMPARE(proxy->xPosRole(), QString("x")); + QCOMPARE(proxy->yPosRole(), QString("y")); + QCOMPARE(proxy->zPosRole(), QString("z")); + QCOMPARE(proxy->rotationRole(), QString("rot")); + delete proxy; } void tst_proxy::initialProperties() diff --git a/tests/auto/cpptest/q3dscatter-series/tst_series.cpp b/tests/auto/cpptest/q3dscatter-series/tst_series.cpp index 80db0ac8..df290579 100644 --- a/tests/auto/cpptest/q3dscatter-series/tst_series.cpp +++ b/tests/auto/cpptest/q3dscatter-series/tst_series.cpp @@ -36,7 +36,6 @@ private slots: void initialProperties(); void initializeProperties(); - void invalidProperties(); private: QScatter3DSeries *m_series; @@ -65,20 +64,46 @@ void tst_series::construct() QScatter3DSeries *series = new QScatter3DSeries(); QVERIFY(series); delete series; + + QScatterDataProxy *proxy = new QScatterDataProxy(); + + series = new QScatter3DSeries(proxy); + QVERIFY(series); + QCOMPARE(series->dataProxy(), proxy); + delete series; } void tst_series::initialProperties() { QVERIFY(m_series); + + QVERIFY(m_series->dataProxy()); + QCOMPARE(m_series->itemSize(), 0.0f); + QCOMPARE(m_series->selectedItem(), m_series->invalidSelectionIndex()); + + // Common properties. The ones identical between different series are tested in QBar3DSeries tests + QCOMPARE(m_series->itemLabelFormat(), QString("@xLabel, @yLabel, @zLabel")); + QCOMPARE(m_series->mesh(), QAbstract3DSeries::MeshSphere); + QCOMPARE(m_series->type(), QAbstract3DSeries::SeriesTypeScatter); } void tst_series::initializeProperties() { QVERIFY(m_series); -} -void tst_series::invalidProperties() -{ + m_series->setDataProxy(new QScatterDataProxy()); + m_series->setItemSize(0.5f); + m_series->setSelectedItem(0); + + QCOMPARE(m_series->itemSize(), 0.5f); + QCOMPARE(m_series->selectedItem(), 0); + + // Common properties. The ones identical between different series are tested in QBar3DSeries tests + m_series->setMesh(QAbstract3DSeries::MeshPoint); + m_series->setMeshRotation(QQuaternion(1, 1, 10, 20)); + + QCOMPARE(m_series->mesh(), QAbstract3DSeries::MeshPoint); + QCOMPARE(m_series->meshRotation(), QQuaternion(1, 1, 10, 20)); } QTEST_MAIN(tst_series) diff --git a/tests/auto/cpptest/q3dscatter/tst_scatter.cpp b/tests/auto/cpptest/q3dscatter/tst_scatter.cpp index 0bbc2122..89057788 100644 --- a/tests/auto/cpptest/q3dscatter/tst_scatter.cpp +++ b/tests/auto/cpptest/q3dscatter/tst_scatter.cpp @@ -80,6 +80,10 @@ void tst_scatter::construct() Q3DScatter *graph = new Q3DScatter(); QVERIFY(graph); delete graph; + + graph = new Q3DScatter(new QSurfaceFormat()); + QVERIFY(graph); + delete graph; } void tst_scatter::initialProperties() diff --git a/tests/auto/cpptest/q3dsurface-heightproxy/tst_proxy.cpp b/tests/auto/cpptest/q3dsurface-heightproxy/tst_proxy.cpp index 8e998568..20ed1aeb 100644 --- a/tests/auto/cpptest/q3dsurface-heightproxy/tst_proxy.cpp +++ b/tests/auto/cpptest/q3dsurface-heightproxy/tst_proxy.cpp @@ -65,6 +65,20 @@ void tst_proxy::construct() QHeightMapSurfaceDataProxy *proxy = new QHeightMapSurfaceDataProxy(); QVERIFY(proxy); delete proxy; + + proxy = new QHeightMapSurfaceDataProxy(QImage(QSize(10, 10), QImage::Format_ARGB32)); + QVERIFY(proxy); + QCoreApplication::processEvents(); + QCOMPARE(proxy->columnCount(), 10); + QCOMPARE(proxy->rowCount(), 10); + delete proxy; + + proxy = new QHeightMapSurfaceDataProxy(":/customtexture.jpg"); + QVERIFY(proxy); + QCoreApplication::processEvents(); + QCOMPARE(proxy->columnCount(), 24); + QCOMPARE(proxy->rowCount(), 24); + delete proxy; } void tst_proxy::initialProperties() diff --git a/tests/auto/cpptest/q3dsurface-modelproxy/tst_proxy.cpp b/tests/auto/cpptest/q3dsurface-modelproxy/tst_proxy.cpp index f7311ca1..6bef9478 100644 --- a/tests/auto/cpptest/q3dsurface-modelproxy/tst_proxy.cpp +++ b/tests/auto/cpptest/q3dsurface-modelproxy/tst_proxy.cpp @@ -63,11 +63,75 @@ void tst_proxy::cleanup() delete m_proxy; } + void tst_proxy::construct() { QItemModelSurfaceDataProxy *proxy = new QItemModelSurfaceDataProxy(); QVERIFY(proxy); delete proxy; + + QTableWidget *table = new QTableWidget(); + + proxy = new QItemModelSurfaceDataProxy(table->model()); + QVERIFY(proxy); + delete proxy; + + proxy = new QItemModelSurfaceDataProxy(table->model(), "y"); + QVERIFY(proxy); + QCOMPARE(proxy->rowRole(), QString("")); + QCOMPARE(proxy->columnRole(), QString("")); + QCOMPARE(proxy->xPosRole(), QString("")); + QCOMPARE(proxy->yPosRole(), QString("y")); + QCOMPARE(proxy->zPosRole(), QString("")); + QCOMPARE(proxy->rowCategories().length(), 0); + QCOMPARE(proxy->columnCategories().length(), 0); + delete proxy; + + proxy = new QItemModelSurfaceDataProxy(table->model(), "row", "column", "y"); + QVERIFY(proxy); + QCOMPARE(proxy->rowRole(), QString("row")); + QCOMPARE(proxy->columnRole(), QString("column")); + QCOMPARE(proxy->xPosRole(), QString("column")); + QCOMPARE(proxy->yPosRole(), QString("y")); + QCOMPARE(proxy->zPosRole(), QString("row")); + QCOMPARE(proxy->rowCategories().length(), 0); + QCOMPARE(proxy->columnCategories().length(), 0); + delete proxy; + + proxy = new QItemModelSurfaceDataProxy(table->model(), "row", "column", "x", "y", "z"); + QVERIFY(proxy); + QCOMPARE(proxy->rowRole(), QString("row")); + QCOMPARE(proxy->columnRole(), QString("column")); + QCOMPARE(proxy->xPosRole(), QString("x")); + QCOMPARE(proxy->yPosRole(), QString("y")); + QCOMPARE(proxy->zPosRole(), QString("z")); + QCOMPARE(proxy->rowCategories().length(), 0); + QCOMPARE(proxy->columnCategories().length(), 0); + delete proxy; + + proxy = new QItemModelSurfaceDataProxy(table->model(), "row", "column", "y", + QStringList() << "rowCat", QStringList() << "colCat"); + QVERIFY(proxy); + QCOMPARE(proxy->rowRole(), QString("row")); + QCOMPARE(proxy->columnRole(), QString("column")); + QCOMPARE(proxy->xPosRole(), QString("column")); + QCOMPARE(proxy->yPosRole(), QString("y")); + QCOMPARE(proxy->zPosRole(), QString("row")); + QCOMPARE(proxy->rowCategories().length(), 1); + QCOMPARE(proxy->columnCategories().length(), 1); + delete proxy; + + proxy = new QItemModelSurfaceDataProxy(table->model(), "row", "column", "x", "y", "z", + QStringList() << "rowCat", QStringList() << "colCat"); + QVERIFY(proxy); + QCOMPARE(proxy->rowRole(), QString("row")); + QCOMPARE(proxy->columnRole(), QString("column")); + QCOMPARE(proxy->xPosRole(), QString("x")); + QCOMPARE(proxy->yPosRole(), QString("y")); + QCOMPARE(proxy->zPosRole(), QString("z")); + QCOMPARE(proxy->rowCategories().length(), 1); + QCOMPARE(proxy->columnCategories().length(), 1); + delete proxy; } void tst_proxy::initialProperties() diff --git a/tests/auto/cpptest/q3dsurface-series/tst_series.cpp b/tests/auto/cpptest/q3dsurface-series/tst_series.cpp index 95c3a7df..50eed686 100644 --- a/tests/auto/cpptest/q3dsurface-series/tst_series.cpp +++ b/tests/auto/cpptest/q3dsurface-series/tst_series.cpp @@ -65,20 +65,55 @@ void tst_series::construct() QSurface3DSeries *series = new QSurface3DSeries(); QVERIFY(series); delete series; + + QSurfaceDataProxy *proxy = new QSurfaceDataProxy(); + + series = new QSurface3DSeries(proxy); + QVERIFY(series); + QCOMPARE(series->dataProxy(), proxy); + delete series; } void tst_series::initialProperties() { QVERIFY(m_series); + + QVERIFY(m_series->dataProxy()); + QCOMPARE(m_series->drawMode(), QSurface3DSeries::DrawSurfaceAndWireframe); + QCOMPARE(m_series->isFlatShadingEnabled(), true); + QCOMPARE(m_series->isFlatShadingSupported(), true); + QCOMPARE(m_series->selectedPoint(), m_series->invalidSelectionPosition()); + + // Common properties. The ones identical between different series are tested in QBar3DSeries tests + QCOMPARE(m_series->itemLabelFormat(), QString("@xLabel, @yLabel, @zLabel")); + QCOMPARE(m_series->mesh(), QAbstract3DSeries::MeshSphere); + QCOMPARE(m_series->type(), QAbstract3DSeries::SeriesTypeSurface); } void tst_series::initializeProperties() { QVERIFY(m_series); + + m_series->setDataProxy(new QSurfaceDataProxy()); + m_series->setDrawMode(QSurface3DSeries::DrawWireframe); + m_series->setFlatShadingEnabled(false); + m_series->setSelectedPoint(QPoint(0, 0)); + + QCOMPARE(m_series->drawMode(), QSurface3DSeries::DrawWireframe); + QCOMPARE(m_series->isFlatShadingEnabled(), false); + QCOMPARE(m_series->selectedPoint(), QPoint(0, 0)); + + // Common properties. The ones identical between different series are tested in QBar3DSeries tests + m_series->setMesh(QAbstract3DSeries::MeshPyramid); + + QCOMPARE(m_series->mesh(), QAbstract3DSeries::MeshPyramid); } void tst_series::invalidProperties() { + m_series->setMesh(QAbstract3DSeries::MeshPoint); + + QCOMPARE(m_series->mesh(), QAbstract3DSeries::MeshSphere); } QTEST_MAIN(tst_series) diff --git a/tests/auto/cpptest/q3dsurface/tst_surface.cpp b/tests/auto/cpptest/q3dsurface/tst_surface.cpp index 7b2d3ebb..600261d2 100644 --- a/tests/auto/cpptest/q3dsurface/tst_surface.cpp +++ b/tests/auto/cpptest/q3dsurface/tst_surface.cpp @@ -85,6 +85,10 @@ void tst_surface::construct() Q3DSurface *graph = new Q3DSurface(); QVERIFY(graph); delete graph; + + graph = new Q3DSurface(new QSurfaceFormat()); + QVERIFY(graph); + delete graph; } void tst_surface::initialProperties() -- cgit v1.2.3 From 208cb0638f27dc3897034b3d01dec45843eb12db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomi=20Korpip=C3=A4=C3=A4?= Date: Tue, 21 Oct 2014 13:10:49 +0300 Subject: Added autotests for C++ axes Task-number: QTRD-3368 Change-Id: I79f49ba839f191b64206f38763b2afff167757b7 Change-Id: I79f49ba839f191b64206f38763b2afff167757b7 Reviewed-by: Mika Salmela --- tests/auto/cpptest/cpptest.pro | 5 +- .../cpptest/q3daxis-category/q3daxis-category.pro | 8 ++ tests/auto/cpptest/q3daxis-category/tst_axis.cpp | 133 +++++++++++++++++ .../cpptest/q3daxis-logvalue/q3daxis-logvalue.pro | 8 ++ tests/auto/cpptest/q3daxis-logvalue/tst_axis.cpp | 102 +++++++++++++ tests/auto/cpptest/q3daxis-value/q3daxis-value.pro | 8 ++ tests/auto/cpptest/q3daxis-value/tst_axis.cpp | 158 +++++++++++++++++++++ tests/auto/cpptest/q3dbars/tst_bars.cpp | 3 + tests/auto/cpptest/q3dscatter/tst_scatter.cpp | 3 + tests/auto/cpptest/q3dsurface/tst_surface.cpp | 3 + 10 files changed, 430 insertions(+), 1 deletion(-) create mode 100644 tests/auto/cpptest/q3daxis-category/q3daxis-category.pro create mode 100644 tests/auto/cpptest/q3daxis-category/tst_axis.cpp create mode 100644 tests/auto/cpptest/q3daxis-logvalue/q3daxis-logvalue.pro create mode 100644 tests/auto/cpptest/q3daxis-logvalue/tst_axis.cpp create mode 100644 tests/auto/cpptest/q3daxis-value/q3daxis-value.pro create mode 100644 tests/auto/cpptest/q3daxis-value/tst_axis.cpp diff --git a/tests/auto/cpptest/cpptest.pro b/tests/auto/cpptest/cpptest.pro index 29ba3dc3..578f5c53 100644 --- a/tests/auto/cpptest/cpptest.pro +++ b/tests/auto/cpptest/cpptest.pro @@ -11,4 +11,7 @@ SUBDIRS = q3dbars \ q3dsurface-proxy \ q3dsurface-modelproxy \ q3dsurface-heightproxy \ - q3dsurface-series + q3dsurface-series \ + q3daxis-category \ + q3daxis-logvalue \ + q3daxis-value diff --git a/tests/auto/cpptest/q3daxis-category/q3daxis-category.pro b/tests/auto/cpptest/q3daxis-category/q3daxis-category.pro new file mode 100644 index 00000000..74415397 --- /dev/null +++ b/tests/auto/cpptest/q3daxis-category/q3daxis-category.pro @@ -0,0 +1,8 @@ +QT += testlib datavisualization + +TARGET = tst_cpptest +CONFIG += console testcase + +TEMPLATE = app + +SOURCES += tst_axis.cpp diff --git a/tests/auto/cpptest/q3daxis-category/tst_axis.cpp b/tests/auto/cpptest/q3daxis-category/tst_axis.cpp new file mode 100644 index 00000000..800a0953 --- /dev/null +++ b/tests/auto/cpptest/q3daxis-category/tst_axis.cpp @@ -0,0 +1,133 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +#include + +#include + +using namespace QtDataVisualization; + +class tst_axis: public QObject +{ + Q_OBJECT + +private slots: + void initTestCase(); + void cleanupTestCase(); + void init(); + void cleanup(); + + void construct(); + + void initialProperties(); + void initializeProperties(); + void invalidProperties(); + +private: + QCategory3DAxis *m_axis; +}; + +void tst_axis::initTestCase() +{ +} + +void tst_axis::cleanupTestCase() +{ +} + +void tst_axis::init() +{ + m_axis = new QCategory3DAxis(); +} + +void tst_axis::cleanup() +{ + delete m_axis; +} + +void tst_axis::construct() +{ + QCategory3DAxis *axis = new QCategory3DAxis(); + QVERIFY(axis); + delete axis; +} + +void tst_axis::initialProperties() +{ + QVERIFY(m_axis); + + QCOMPARE(m_axis->labels().length(), 0); + + // Common (from QAbstract3DAxis) + QCOMPARE(m_axis->isAutoAdjustRange(), true); + QCOMPARE(m_axis->labelAutoRotation(), 0.0f); + QCOMPARE(m_axis->max(), 10.0f); + QCOMPARE(m_axis->min(), 0.0f); + QCOMPARE(m_axis->orientation(), QAbstract3DAxis::AxisOrientationNone); + QCOMPARE(m_axis->title(), QString("")); + QCOMPARE(m_axis->isTitleFixed(), true); + QCOMPARE(m_axis->isTitleVisible(), false); + QCOMPARE(m_axis->type(), QAbstract3DAxis::AxisTypeCategory); +} + +void tst_axis::initializeProperties() +{ + QVERIFY(m_axis); + + m_axis->setLabels(QStringList() << "first" << "second"); + + QCOMPARE(m_axis->labels().length(), 2); + QCOMPARE(m_axis->labels().at(1), QString("second")); + + // Common (from QAbstract3DAxis) + m_axis->setAutoAdjustRange(false); + m_axis->setLabelAutoRotation(15.0f); + m_axis->setMax(25.0f); + m_axis->setMin(5.0f); + m_axis->setTitle("title"); + m_axis->setTitleFixed(false); + m_axis->setTitleVisible(true); + + QCOMPARE(m_axis->isAutoAdjustRange(), false); + QCOMPARE(m_axis->labelAutoRotation(), 15.0f); + QCOMPARE(m_axis->max(), 25.0f); + QCOMPARE(m_axis->min(), 5.0f); + QCOMPARE(m_axis->title(), QString("title")); + QCOMPARE(m_axis->isTitleFixed(), false); + QCOMPARE(m_axis->isTitleVisible(), true); +} + +void tst_axis::invalidProperties() +{ + m_axis->setLabelAutoRotation(-15.0f); + QCOMPARE(m_axis->labelAutoRotation(), 0.0f); + + m_axis->setLabelAutoRotation(100.0f); + QCOMPARE(m_axis->labelAutoRotation(), 90.0f); + + m_axis->setMax(-10.0f); + QCOMPARE(m_axis->max(), 0.0f); + QCOMPARE(m_axis->min(), 0.0f); + + m_axis->setMin(10.0f); + QCOMPARE(m_axis->max(), 11.0f); + QCOMPARE(m_axis->min(), 10.0f); +} + +QTEST_MAIN(tst_axis) +#include "tst_axis.moc" diff --git a/tests/auto/cpptest/q3daxis-logvalue/q3daxis-logvalue.pro b/tests/auto/cpptest/q3daxis-logvalue/q3daxis-logvalue.pro new file mode 100644 index 00000000..74415397 --- /dev/null +++ b/tests/auto/cpptest/q3daxis-logvalue/q3daxis-logvalue.pro @@ -0,0 +1,8 @@ +QT += testlib datavisualization + +TARGET = tst_cpptest +CONFIG += console testcase + +TEMPLATE = app + +SOURCES += tst_axis.cpp diff --git a/tests/auto/cpptest/q3daxis-logvalue/tst_axis.cpp b/tests/auto/cpptest/q3daxis-logvalue/tst_axis.cpp new file mode 100644 index 00000000..adbedb24 --- /dev/null +++ b/tests/auto/cpptest/q3daxis-logvalue/tst_axis.cpp @@ -0,0 +1,102 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +#include + +#include + +using namespace QtDataVisualization; + +class tst_axis: public QObject +{ + Q_OBJECT + +private slots: + void initTestCase(); + void cleanupTestCase(); + void init(); + void cleanup(); + + void construct(); + + void initialProperties(); + void initializeProperties(); + void invalidProperties(); + +private: + QLogValue3DAxisFormatter *m_formatter; +}; + +void tst_axis::initTestCase() +{ +} + +void tst_axis::cleanupTestCase() +{ +} + +void tst_axis::init() +{ + m_formatter = new QLogValue3DAxisFormatter(); +} + +void tst_axis::cleanup() +{ + delete m_formatter; +} + +void tst_axis::construct() +{ + QLogValue3DAxisFormatter *formatter = new QLogValue3DAxisFormatter(); + QVERIFY(formatter); + delete formatter; +} + +void tst_axis::initialProperties() +{ + QVERIFY(m_formatter); + + QCOMPARE(m_formatter->autoSubGrid(), true); + QCOMPARE(m_formatter->base(), 10.0); + QCOMPARE(m_formatter->showEdgeLabels(), true); +} + +void tst_axis::initializeProperties() +{ + QVERIFY(m_formatter); + + m_formatter->setAutoSubGrid(false); + m_formatter->setBase(0.1); + m_formatter->setShowEdgeLabels(false); + + QCOMPARE(m_formatter->autoSubGrid(), false); + QCOMPARE(m_formatter->base(), 0.1); + QCOMPARE(m_formatter->showEdgeLabels(), false); +} + +void tst_axis::invalidProperties() +{ + m_formatter->setBase(-1.0); + QCOMPARE(m_formatter->base(), 10.0); + + m_formatter->setBase(1.0); + QCOMPARE(m_formatter->base(), 10.0); +} + +QTEST_MAIN(tst_axis) +#include "tst_axis.moc" diff --git a/tests/auto/cpptest/q3daxis-value/q3daxis-value.pro b/tests/auto/cpptest/q3daxis-value/q3daxis-value.pro new file mode 100644 index 00000000..74415397 --- /dev/null +++ b/tests/auto/cpptest/q3daxis-value/q3daxis-value.pro @@ -0,0 +1,8 @@ +QT += testlib datavisualization + +TARGET = tst_cpptest +CONFIG += console testcase + +TEMPLATE = app + +SOURCES += tst_axis.cpp diff --git a/tests/auto/cpptest/q3daxis-value/tst_axis.cpp b/tests/auto/cpptest/q3daxis-value/tst_axis.cpp new file mode 100644 index 00000000..92029f13 --- /dev/null +++ b/tests/auto/cpptest/q3daxis-value/tst_axis.cpp @@ -0,0 +1,158 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +#include + +#include + +using namespace QtDataVisualization; + +class tst_axis: public QObject +{ + Q_OBJECT + +private slots: + void initTestCase(); + void cleanupTestCase(); + void init(); + void cleanup(); + + void construct(); + + void initialProperties(); + void initializeProperties(); + void invalidProperties(); + +private: + QValue3DAxis *m_axis; +}; + +void tst_axis::initTestCase() +{ +} + +void tst_axis::cleanupTestCase() +{ +} + +void tst_axis::init() +{ + m_axis = new QValue3DAxis(); +} + +void tst_axis::cleanup() +{ + delete m_axis; +} + +void tst_axis::construct() +{ + QValue3DAxis *axis = new QValue3DAxis(); + QVERIFY(axis); + delete axis; +} + +void tst_axis::initialProperties() +{ + QVERIFY(m_axis); + + QCOMPARE(m_axis->labelFormat(), QString("%.2f")); + QCOMPARE(m_axis->reversed(), false); + QCOMPARE(m_axis->segmentCount(), 5); + QCOMPARE(m_axis->subSegmentCount(), 1); + + // Common (from QAbstract3DAxis) + QCOMPARE(m_axis->isAutoAdjustRange(), true); + QCOMPARE(m_axis->labelAutoRotation(), 0.0f); + QCOMPARE(m_axis->labels().length(), 6); + QCOMPARE(m_axis->labels().at(0), QString("0.00")); + QCOMPARE(m_axis->labels().at(1), QString("2.00")); + QCOMPARE(m_axis->labels().at(2), QString("4.00")); + QCOMPARE(m_axis->labels().at(3), QString("6.00")); + QCOMPARE(m_axis->labels().at(4), QString("8.00")); + QCOMPARE(m_axis->labels().at(5), QString("10.00")); + QCOMPARE(m_axis->max(), 10.0f); + QCOMPARE(m_axis->min(), 0.0f); + QCOMPARE(m_axis->orientation(), QAbstract3DAxis::AxisOrientationNone); + QCOMPARE(m_axis->title(), QString("")); + QCOMPARE(m_axis->isTitleFixed(), true); + QCOMPARE(m_axis->isTitleVisible(), false); + QCOMPARE(m_axis->type(), QAbstract3DAxis::AxisTypeValue); +} + +void tst_axis::initializeProperties() +{ + QVERIFY(m_axis); + + m_axis->setLabelFormat("%.0fm"); + m_axis->setReversed(true); + m_axis->setSegmentCount(2); + m_axis->setSubSegmentCount(5); + + QCOMPARE(m_axis->labelFormat(), QString("%.0fm")); + QCOMPARE(m_axis->reversed(), true); + QCOMPARE(m_axis->segmentCount(), 2); + QCOMPARE(m_axis->subSegmentCount(), 5); + + // Common (from QAbstract3DAxis) + m_axis->setAutoAdjustRange(false); + m_axis->setLabelAutoRotation(15.0f); + m_axis->setMax(25.0f); + m_axis->setMin(5.0f); + m_axis->setTitle("title"); + m_axis->setTitleFixed(false); + m_axis->setTitleVisible(true); + + QCOMPARE(m_axis->isAutoAdjustRange(), false); + QCOMPARE(m_axis->labelAutoRotation(), 15.0f); + QCOMPARE(m_axis->labels().length(), 3); + QCOMPARE(m_axis->labels().at(0), QString("5m")); + QCOMPARE(m_axis->labels().at(1), QString("15m")); + QCOMPARE(m_axis->labels().at(2), QString("25m")); + QCOMPARE(m_axis->max(), 25.0f); + QCOMPARE(m_axis->min(), 5.0f); + QCOMPARE(m_axis->title(), QString("title")); + QCOMPARE(m_axis->isTitleFixed(), false); + QCOMPARE(m_axis->isTitleVisible(), true); +} + +void tst_axis::invalidProperties() +{ + m_axis->setSegmentCount(-1); + QCOMPARE(m_axis->segmentCount(), 1); + + m_axis->setSubSegmentCount(-1); + QCOMPARE(m_axis->subSegmentCount(), 1); + + m_axis->setLabelAutoRotation(-15.0f); + QCOMPARE(m_axis->labelAutoRotation(), 0.0f); + + m_axis->setLabelAutoRotation(100.0f); + QCOMPARE(m_axis->labelAutoRotation(), 90.0f); + + m_axis->setMax(-10.0f); + QCOMPARE(m_axis->max(), -10.0f); + QCOMPARE(m_axis->min(), -11.0f); + + m_axis->setMin(10.0f); + QCOMPARE(m_axis->max(), 11.0f); + QCOMPARE(m_axis->min(), 10.0f); +} + +QTEST_MAIN(tst_axis) +#include "tst_axis.moc" diff --git a/tests/auto/cpptest/q3dbars/tst_bars.cpp b/tests/auto/cpptest/q3dbars/tst_bars.cpp index 4535c026..6910e4fa 100644 --- a/tests/auto/cpptest/q3dbars/tst_bars.cpp +++ b/tests/auto/cpptest/q3dbars/tst_bars.cpp @@ -112,6 +112,9 @@ void tst_bars::initialProperties() QVERIFY(!m_graph->selectedSeries()); QVERIFY(!m_graph->primarySeries()); QCOMPARE(m_graph->floorLevel(), 0.0); + QCOMPARE(m_graph->columnAxis()->orientation(), QAbstract3DAxis::AxisOrientationX); + QCOMPARE(m_graph->valueAxis()->orientation(), QAbstract3DAxis::AxisOrientationY); + QCOMPARE(m_graph->rowAxis()->orientation(), QAbstract3DAxis::AxisOrientationZ); // Common properties QCOMPARE(m_graph->activeTheme()->type(), Q3DTheme::ThemeQt); diff --git a/tests/auto/cpptest/q3dscatter/tst_scatter.cpp b/tests/auto/cpptest/q3dscatter/tst_scatter.cpp index 89057788..5a3b6550 100644 --- a/tests/auto/cpptest/q3dscatter/tst_scatter.cpp +++ b/tests/auto/cpptest/q3dscatter/tst_scatter.cpp @@ -91,6 +91,9 @@ void tst_scatter::initialProperties() QVERIFY(m_graph); QCOMPARE(m_graph->seriesList().length(), 0); QVERIFY(!m_graph->selectedSeries()); + QCOMPARE(m_graph->axisX()->orientation(), QAbstract3DAxis::AxisOrientationX); + QCOMPARE(m_graph->axisY()->orientation(), QAbstract3DAxis::AxisOrientationY); + QCOMPARE(m_graph->axisZ()->orientation(), QAbstract3DAxis::AxisOrientationZ); // Common properties QCOMPARE(m_graph->activeTheme()->type(), Q3DTheme::ThemeQt); diff --git a/tests/auto/cpptest/q3dsurface/tst_surface.cpp b/tests/auto/cpptest/q3dsurface/tst_surface.cpp index 600261d2..0ae0a326 100644 --- a/tests/auto/cpptest/q3dsurface/tst_surface.cpp +++ b/tests/auto/cpptest/q3dsurface/tst_surface.cpp @@ -97,6 +97,9 @@ void tst_surface::initialProperties() QCOMPARE(m_graph->seriesList().length(), 0); QVERIFY(!m_graph->selectedSeries()); QCOMPARE(m_graph->flipHorizontalGrid(), false); + QCOMPARE(m_graph->axisX()->orientation(), QAbstract3DAxis::AxisOrientationX); + QCOMPARE(m_graph->axisY()->orientation(), QAbstract3DAxis::AxisOrientationY); + QCOMPARE(m_graph->axisZ()->orientation(), QAbstract3DAxis::AxisOrientationZ); // Common properties QCOMPARE(m_graph->activeTheme()->type(), Q3DTheme::ThemeQt); -- cgit v1.2.3 From 6be168da13a8d66f4b22e3e39ebdd72c657d0ebe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomi=20Korpip=C3=A4=C3=A4?= Date: Wed, 22 Oct 2014 08:50:14 +0300 Subject: Added C++ autotests for scene, light and camera Task-number: QTRD-3368 Change-Id: I6cd7a73477ac34d025c9bdcd7b775abab7d1426c Change-Id: I6cd7a73477ac34d025c9bdcd7b775abab7d1426c Reviewed-by: Miikka Heikkinen --- tests/auto/cpptest/cpptest.pro | 5 +- .../cpptest/q3dscene-camera/q3dscene-camera.pro | 8 + tests/auto/cpptest/q3dscene-camera/tst_camera.cpp | 179 +++++++++++++++++++++ .../auto/cpptest/q3dscene-light/q3dscene-light.pro | 8 + tests/auto/cpptest/q3dscene-light/tst_light.cpp | 92 +++++++++++ tests/auto/cpptest/q3dscene/q3dscene.pro | 8 + tests/auto/cpptest/q3dscene/tst_scene.cpp | 170 +++++++++++++++++++ 7 files changed, 469 insertions(+), 1 deletion(-) create mode 100644 tests/auto/cpptest/q3dscene-camera/q3dscene-camera.pro create mode 100644 tests/auto/cpptest/q3dscene-camera/tst_camera.cpp create mode 100644 tests/auto/cpptest/q3dscene-light/q3dscene-light.pro create mode 100644 tests/auto/cpptest/q3dscene-light/tst_light.cpp create mode 100644 tests/auto/cpptest/q3dscene/q3dscene.pro create mode 100644 tests/auto/cpptest/q3dscene/tst_scene.cpp diff --git a/tests/auto/cpptest/cpptest.pro b/tests/auto/cpptest/cpptest.pro index 578f5c53..9c8d944a 100644 --- a/tests/auto/cpptest/cpptest.pro +++ b/tests/auto/cpptest/cpptest.pro @@ -14,4 +14,7 @@ SUBDIRS = q3dbars \ q3dsurface-series \ q3daxis-category \ q3daxis-logvalue \ - q3daxis-value + q3daxis-value \ + q3dscene \ + q3dscene-camera \ + q3dscene-light diff --git a/tests/auto/cpptest/q3dscene-camera/q3dscene-camera.pro b/tests/auto/cpptest/q3dscene-camera/q3dscene-camera.pro new file mode 100644 index 00000000..c575a55e --- /dev/null +++ b/tests/auto/cpptest/q3dscene-camera/q3dscene-camera.pro @@ -0,0 +1,8 @@ +QT += testlib datavisualization + +TARGET = tst_cpptest +CONFIG += console testcase + +TEMPLATE = app + +SOURCES += tst_camera.cpp diff --git a/tests/auto/cpptest/q3dscene-camera/tst_camera.cpp b/tests/auto/cpptest/q3dscene-camera/tst_camera.cpp new file mode 100644 index 00000000..ee321b22 --- /dev/null +++ b/tests/auto/cpptest/q3dscene-camera/tst_camera.cpp @@ -0,0 +1,179 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +#include + +#include + +using namespace QtDataVisualization; + +class tst_camera: public QObject +{ + Q_OBJECT + +private slots: + void initTestCase(); + void cleanupTestCase(); + void init(); + void cleanup(); + + void construct(); + + void initialProperties(); + void initializeProperties(); + void invalidProperties(); + + void changePresets(); + +private: + Q3DCamera *m_camera; +}; + +void tst_camera::initTestCase() +{ +} + +void tst_camera::cleanupTestCase() +{ +} + +void tst_camera::init() +{ + m_camera = new Q3DCamera(); +} + +void tst_camera::cleanup() +{ + delete m_camera; +} + +void tst_camera::construct() +{ + Q3DCamera *camera = new Q3DCamera(); + QVERIFY(camera); + delete camera; +} + +void tst_camera::initialProperties() +{ + QVERIFY(m_camera); + + QCOMPARE(m_camera->cameraPreset(), Q3DCamera::CameraPresetNone); + QCOMPARE(m_camera->maxZoomLevel(), 500.0f); + QCOMPARE(m_camera->minZoomLevel(), 10.0f); + QCOMPARE(m_camera->target(), QVector3D(0.0, 0.0, 0.0)); + QCOMPARE(m_camera->wrapXRotation(), true); + QCOMPARE(m_camera->wrapYRotation(), false); + QCOMPARE(m_camera->xRotation(), 0.0f); + QCOMPARE(m_camera->yRotation(), 0.0f); + QCOMPARE(m_camera->zoomLevel(), 100.0f); + + // Common (from Q3DObject) + QVERIFY(!m_camera->parentScene()); + QCOMPARE(m_camera->position(), QVector3D(0, 0, 0)); +} + +void tst_camera::initializeProperties() +{ + QVERIFY(m_camera); + + m_camera->setMaxZoomLevel(1000.0f); + m_camera->setMinZoomLevel(100.0f); + m_camera->setTarget(QVector3D(1.0, -1.0, 1.0)); + m_camera->setWrapXRotation(false); + m_camera->setWrapYRotation(true); + m_camera->setXRotation(30.0f); + m_camera->setYRotation(30.0f); + m_camera->setZoomLevel(500.0f); + + QCOMPARE(m_camera->maxZoomLevel(), 1000.0f); + QCOMPARE(m_camera->minZoomLevel(), 100.0f); + QCOMPARE(m_camera->target(), QVector3D(1.0, -1.0, 1.0)); + QCOMPARE(m_camera->wrapXRotation(), false); + QCOMPARE(m_camera->wrapYRotation(), true); + QCOMPARE(m_camera->xRotation(), 30.0f); + QCOMPARE(m_camera->yRotation(), 30.0f); + QCOMPARE(m_camera->zoomLevel(), 500.0f); + + m_camera->setPosition(QVector3D(1.0, 1.0, 1.0)); + + // Common (from Q3DObject) + QCOMPARE(m_camera->position(), QVector3D(1.0, 1.0, 1.0)); +} + +void tst_camera::invalidProperties() +{ + m_camera->setTarget(QVector3D(-1.5, -1.5, -1.5)); + QCOMPARE(m_camera->target(), QVector3D(-1.0, -1.0, -1.0)); + + m_camera->setTarget(QVector3D(1.5, 1.5, 1.5)); + QCOMPARE(m_camera->target(), QVector3D(1.0, 1.0, 1.0)); + + m_camera->setMinZoomLevel(0.1f); + QCOMPARE(m_camera->minZoomLevel(), 1.0f); +} + +void tst_camera::changePresets() +{ + m_camera->setCameraPreset(Q3DCamera::CameraPresetBehind); // Will be overridden by the the following sets + m_camera->setMaxZoomLevel(1000.0f); + m_camera->setMinZoomLevel(100.0f); + m_camera->setTarget(QVector3D(1.0, -1.0, 1.0)); + m_camera->setWrapXRotation(false); + m_camera->setWrapYRotation(true); + m_camera->setXRotation(30.0f); + m_camera->setYRotation(30.0f); + m_camera->setZoomLevel(500.0f); + + QCOMPARE(m_camera->cameraPreset(), Q3DCamera::CameraPresetNone); + QCOMPARE(m_camera->maxZoomLevel(), 1000.0f); + QCOMPARE(m_camera->minZoomLevel(), 100.0f); + QCOMPARE(m_camera->target(), QVector3D(1.0, -1.0, 1.0)); + QCOMPARE(m_camera->wrapXRotation(), false); + QCOMPARE(m_camera->wrapYRotation(), true); + QCOMPARE(m_camera->xRotation(), 30.0f); + QCOMPARE(m_camera->yRotation(), 30.0f); + QCOMPARE(m_camera->zoomLevel(), 500.0f); + + m_camera->setCameraPreset(Q3DCamera::CameraPresetBehind); // Sets target and rotations + + QCOMPARE(m_camera->cameraPreset(), Q3DCamera::CameraPresetBehind); + QCOMPARE(m_camera->maxZoomLevel(), 1000.0f); + QCOMPARE(m_camera->minZoomLevel(), 100.0f); + QCOMPARE(m_camera->target(), QVector3D(0.0, 0.0, 0.0)); + QCOMPARE(m_camera->wrapXRotation(), false); + QCOMPARE(m_camera->wrapYRotation(), true); + QCOMPARE(m_camera->xRotation(), 180.0f); + QCOMPARE(m_camera->yRotation(), 22.5f); + QCOMPARE(m_camera->zoomLevel(), 500.0f); + + m_camera->setCameraPosition(10.0f, 15.0f, 125.0f); // Overrides preset + + QCOMPARE(m_camera->cameraPreset(), Q3DCamera::CameraPresetNone); + QCOMPARE(m_camera->maxZoomLevel(), 1000.0f); + QCOMPARE(m_camera->minZoomLevel(), 100.0f); + QCOMPARE(m_camera->target(), QVector3D(0.0, 0.0, 0.0)); + QCOMPARE(m_camera->wrapXRotation(), false); + QCOMPARE(m_camera->wrapYRotation(), true); + QCOMPARE(m_camera->xRotation(), 10.0f); + QCOMPARE(m_camera->yRotation(), 15.0f); + QCOMPARE(m_camera->zoomLevel(), 125.0f); +} + +QTEST_MAIN(tst_camera) +#include "tst_camera.moc" diff --git a/tests/auto/cpptest/q3dscene-light/q3dscene-light.pro b/tests/auto/cpptest/q3dscene-light/q3dscene-light.pro new file mode 100644 index 00000000..21a3c934 --- /dev/null +++ b/tests/auto/cpptest/q3dscene-light/q3dscene-light.pro @@ -0,0 +1,8 @@ +QT += testlib datavisualization + +TARGET = tst_cpptest +CONFIG += console testcase + +TEMPLATE = app + +SOURCES += tst_light.cpp diff --git a/tests/auto/cpptest/q3dscene-light/tst_light.cpp b/tests/auto/cpptest/q3dscene-light/tst_light.cpp new file mode 100644 index 00000000..4568b01e --- /dev/null +++ b/tests/auto/cpptest/q3dscene-light/tst_light.cpp @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +#include + +#include + +using namespace QtDataVisualization; + +class tst_light: public QObject +{ + Q_OBJECT + +private slots: + void initTestCase(); + void cleanupTestCase(); + void init(); + void cleanup(); + + void construct(); + + void initialProperties(); + void initializeProperties(); + +private: + Q3DLight *m_light; +}; + +void tst_light::initTestCase() +{ +} + +void tst_light::cleanupTestCase() +{ +} + +void tst_light::init() +{ + m_light = new Q3DLight(); +} + +void tst_light::cleanup() +{ + delete m_light; +} + +void tst_light::construct() +{ + Q3DLight *light = new Q3DLight(); + QVERIFY(light); + delete light; +} + +void tst_light::initialProperties() +{ + QVERIFY(m_light); + + // TODO: Has no adjustable properties yet. + // Keeping this as a placeholder for future implementations (QTRD-2406) + + // Common (from Q3DObject) + QVERIFY(!m_light->parentScene()); + QCOMPARE(m_light->position(), QVector3D(0, 0, 0)); +} + +void tst_light::initializeProperties() +{ + QVERIFY(m_light); + + m_light->setPosition(QVector3D(1.0, 1.0, 1.0)); + + // Common (from Q3DObject) + QCOMPARE(m_light->position(), QVector3D(1.0, 1.0, 1.0)); +} + +QTEST_MAIN(tst_light) +#include "tst_light.moc" diff --git a/tests/auto/cpptest/q3dscene/q3dscene.pro b/tests/auto/cpptest/q3dscene/q3dscene.pro new file mode 100644 index 00000000..b9be69c0 --- /dev/null +++ b/tests/auto/cpptest/q3dscene/q3dscene.pro @@ -0,0 +1,8 @@ +QT += testlib datavisualization + +TARGET = tst_cpptest +CONFIG += console testcase + +TEMPLATE = app + +SOURCES += tst_scene.cpp diff --git a/tests/auto/cpptest/q3dscene/tst_scene.cpp b/tests/auto/cpptest/q3dscene/tst_scene.cpp new file mode 100644 index 00000000..7d1ecad3 --- /dev/null +++ b/tests/auto/cpptest/q3dscene/tst_scene.cpp @@ -0,0 +1,170 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +#include + +#include +#include + +using namespace QtDataVisualization; + +class tst_scene: public QObject +{ + Q_OBJECT + +private slots: + void initTestCase(); + void cleanupTestCase(); + void init(); + void cleanup(); + + void construct(); + + void initialProperties(); + void initializeProperties(); + void invalidProperties(); + + void subViews(); + +private: + Q3DScene *m_scene; +}; + +void tst_scene::initTestCase() +{ +} + +void tst_scene::cleanupTestCase() +{ +} + +void tst_scene::init() +{ + m_scene = new Q3DScene(); +} + +void tst_scene::cleanup() +{ + delete m_scene; +} + +void tst_scene::construct() +{ + Q3DScene *scene = new Q3DScene(); + QVERIFY(scene); + delete scene; +} + +void tst_scene::initialProperties() +{ + QVERIFY(m_scene); + + QVERIFY(m_scene->activeCamera()); + QVERIFY(m_scene->activeLight()); + QCOMPARE(m_scene->devicePixelRatio(), 1.0f); + QCOMPARE(m_scene->graphPositionQuery(), m_scene->invalidSelectionPoint()); + QCOMPARE(m_scene->primarySubViewport(), QRect(0, 0, 0, 0)); + QCOMPARE(m_scene->secondarySubViewport(), QRect(0, 0, 0, 0)); + QCOMPARE(m_scene->isSecondarySubviewOnTop(), true); + QCOMPARE(m_scene->selectionQueryPosition(), m_scene->invalidSelectionPoint()); + QCOMPARE(m_scene->isSlicingActive(), false); + QCOMPARE(m_scene->viewport(), QRect(0, 0, 0, 0)); +} + +void tst_scene::initializeProperties() +{ + QVERIFY(m_scene); + + Q3DCamera *camera1 = new Q3DCamera(); + Q3DLight *light1 = new Q3DLight(); + + m_scene->setActiveCamera(camera1); + m_scene->setActiveLight(light1); + m_scene->setDevicePixelRatio(2.0f); + m_scene->setGraphPositionQuery(QPoint(0, 0)); + m_scene->setPrimarySubViewport(QRect(0, 0, 50, 50)); + m_scene->setSecondarySubViewport(QRect(50, 50, 100, 100)); + m_scene->setSecondarySubviewOnTop(false); + m_scene->setSelectionQueryPosition(QPoint(0, 0)); + m_scene->setSlicingActive(true); + + QCOMPARE(m_scene->activeCamera(), camera1); + QCOMPARE(m_scene->activeLight(), light1); + QCOMPARE(m_scene->devicePixelRatio(), 2.0f); + QCOMPARE(m_scene->graphPositionQuery(), QPoint(0, 0)); // TODO: When doing signal checks, add tests to check that queries return something (asynchronously) + // TODO: subviewports are not set (QTRD-2435) + //QCOMPARE(m_scene->primarySubViewport(), QRect(0, 0, 50, 50)); + //QCOMPARE(m_scene->secondarySubViewport(), QRect(50, 50, 100, 100)); + QCOMPARE(m_scene->isSecondarySubviewOnTop(), false); + QCOMPARE(m_scene->selectionQueryPosition(), QPoint(0, 0)); // TODO: When doing signal checks, add tests to check that queries return something (asynchronously) + QCOMPARE(m_scene->isSlicingActive(), true); + // TODO: viewport is not set by subviewports (QTRD-2435) + //QCOMPARE(m_scene->viewport(), QRect(0, 0, 100, 100)); +} + +void tst_scene::invalidProperties() +{ + m_scene->setPrimarySubViewport(QRect(0, 0, -50, -50)); + m_scene->setSecondarySubViewport(QRect(-50, -50, -100, -100)); + QCOMPARE(m_scene->primarySubViewport(), QRect(0, 0, 0, 0)); + QCOMPARE(m_scene->secondarySubViewport(), QRect(0, 0, 0, 0)); +} + +void tst_scene::subViews() +{ + Q3DBars *graph = new Q3DBars(); + graph->setPosition(QPoint(0, 0)); + graph->setWidth(200); + graph->setHeight(200); + + Q3DScene *scene = graph->scene(); + + QCoreApplication::processEvents(); + + QCOMPARE(scene->viewport(), QRect(0, 0, 200, 200)); + QCOMPARE(scene->primarySubViewport(), QRect(0, 0, 200, 200)); + QCOMPARE(scene->secondarySubViewport(), QRect(0, 0, 0, 0)); + + QCOMPARE(scene->isSecondarySubviewOnTop(), true); + QCOMPARE(scene->isPointInPrimarySubView(QPoint(100, 100)), true); + QCOMPARE(scene->isPointInPrimarySubView(QPoint(201, 201)), false); + QCOMPARE(scene->isPointInSecondarySubView(QPoint(100, 100)), false); + + scene->setSlicingActive(true); + + QCOMPARE(scene->isSecondarySubviewOnTop(), false); + QCOMPARE(scene->primarySubViewport(), QRect(0, 0, 40, 40)); + QCOMPARE(scene->secondarySubViewport(), QRect(0, 0, 200, 200)); + QCOMPARE(scene->isPointInPrimarySubView(QPoint(100, 100)), false); + QCOMPARE(scene->isPointInPrimarySubView(QPoint(30, 30)), true); + QCOMPARE(scene->isPointInSecondarySubView(QPoint(100, 100)), true); + QCOMPARE(scene->isPointInSecondarySubView(QPoint(30, 30)), false); + + scene->setSecondarySubviewOnTop(true); + + QCOMPARE(scene->isSecondarySubviewOnTop(), true); + QCOMPARE(scene->primarySubViewport(), QRect(0, 0, 40, 40)); + QCOMPARE(scene->secondarySubViewport(), QRect(0, 0, 200, 200)); + QCOMPARE(scene->isPointInPrimarySubView(QPoint(100, 100)), false); + QCOMPARE(scene->isPointInPrimarySubView(QPoint(30, 30)), false); + QCOMPARE(scene->isPointInSecondarySubView(QPoint(100, 100)), true); + QCOMPARE(scene->isPointInSecondarySubView(QPoint(30, 30)), true); +} + +QTEST_MAIN(tst_scene) +#include "tst_scene.moc" -- cgit v1.2.3 From 4f9b9e875ce831cdad3c4f303132a6860cf879f8 Mon Sep 17 00:00:00 2001 From: Mika Salmela Date: Wed, 22 Oct 2014 11:00:25 +0300 Subject: Surface shadow improvement Not allowing bias go below 0.001 prevents the shadow acne. Faulty formula for lighning direction. Task-number: QTRD-3382 Change-Id: Ided8bf423a961744ace8bef32ae18d6fa2c443ef Reviewed-by: Miikka Heikkinen --- src/datavisualization/engine/engine.qrc | 1 + .../engine/shaders/surfaceShadowFlat.frag | 4 +- .../engine/shaders/surfaceShadowFlat.vert | 15 +++-- .../engine/shaders/surfaceShadowNoTex.frag | 2 +- .../engine/shaders/surfaceTexturedShadow.frag | 67 ++++++++++++++++++++++ .../engine/shaders/surfaceTexturedShadowFlat.frag | 2 +- src/datavisualization/engine/surface3drenderer.cpp | 2 +- 7 files changed, 80 insertions(+), 13 deletions(-) create mode 100644 src/datavisualization/engine/shaders/surfaceTexturedShadow.frag diff --git a/src/datavisualization/engine/engine.qrc b/src/datavisualization/engine/engine.qrc index 8c234a3b..0ef169c9 100644 --- a/src/datavisualization/engine/engine.qrc +++ b/src/datavisualization/engine/engine.qrc @@ -69,5 +69,6 @@ shaders/3dsliceframes.frag shaders/position.vert shaders/positionmap.frag + shaders/surfaceTexturedShadow.frag diff --git a/src/datavisualization/engine/shaders/surfaceShadowFlat.frag b/src/datavisualization/engine/shaders/surfaceShadowFlat.frag index 4b0dfae0..7eaba7f5 100644 --- a/src/datavisualization/engine/shaders/surfaceShadowFlat.frag +++ b/src/datavisualization/engine/shaders/surfaceShadowFlat.frag @@ -2,7 +2,7 @@ #extension GL_EXT_gpu_shader4 : require -varying highp vec3 coords_mdl; +varying highp vec2 coords_mdl; varying highp vec3 position_wrld; flat varying highp vec3 normal_cmr; varying highp vec3 eyeDirection_cmr; @@ -51,7 +51,7 @@ void main() { highp float cosAlpha = clamp(dot(E, R), 0.0, 1.0); highp float bias = 0.005 * tan(acos(cosTheta)); - bias = clamp(bias, 0.0, 0.01); + bias = clamp(bias, 0.001, 0.01); vec4 shadCoords = shadowCoord; shadCoords.z -= bias; diff --git a/src/datavisualization/engine/shaders/surfaceShadowFlat.vert b/src/datavisualization/engine/shaders/surfaceShadowFlat.vert index d80e062e..98fdde3f 100644 --- a/src/datavisualization/engine/shaders/surfaceShadowFlat.vert +++ b/src/datavisualization/engine/shaders/surfaceShadowFlat.vert @@ -2,10 +2,6 @@ #extension GL_EXT_gpu_shader4 : require -attribute highp vec3 vertexPosition_mdl; -attribute highp vec3 vertexNormal_mdl; -attribute highp vec2 vertexUV; - uniform highp mat4 MVP; uniform highp mat4 V; uniform highp mat4 M; @@ -13,13 +9,17 @@ uniform highp mat4 itM; uniform highp mat4 depthMVP; uniform highp vec3 lightPosition_wrld; +attribute highp vec3 vertexPosition_mdl; +attribute highp vec3 vertexNormal_mdl; +attribute highp vec2 vertexUV; + varying highp vec2 UV; varying highp vec3 position_wrld; flat varying highp vec3 normal_cmr; varying highp vec3 eyeDirection_cmr; varying highp vec3 lightDirection_cmr; varying highp vec4 shadowCoord; -varying highp vec3 coords_mdl; +varying highp vec2 coords_mdl; const highp mat4 bias = mat4(0.5, 0.0, 0.0, 0.0, 0.0, 0.5, 0.0, 0.0, @@ -28,13 +28,12 @@ const highp mat4 bias = mat4(0.5, 0.0, 0.0, 0.0, void main() { gl_Position = MVP * vec4(vertexPosition_mdl, 1.0); - coords_mdl = vertexPosition_mdl; + coords_mdl = vertexPosition_mdl.xy; shadowCoord = bias * depthMVP * vec4(vertexPosition_mdl, 1.0); position_wrld = vec4(M * vec4(vertexPosition_mdl, 1.0)).xyz; vec3 vertexPosition_cmr = vec4(V * M * vec4(vertexPosition_mdl, 1.0)).xyz; eyeDirection_cmr = vec3(0.0, 0.0, 0.0) - vertexPosition_cmr; - vec3 lightPosition_cmr = vec4(V * vec4(lightPosition_wrld, 1.0)).xyz; - lightDirection_cmr = lightPosition_cmr + eyeDirection_cmr; + lightDirection_cmr = vec4(V * vec4(lightPosition_wrld, 0.0)).xyz; normal_cmr = vec4(V * itM * vec4(vertexNormal_mdl, 0.0)).xyz; UV = vertexUV; } diff --git a/src/datavisualization/engine/shaders/surfaceShadowNoTex.frag b/src/datavisualization/engine/shaders/surfaceShadowNoTex.frag index d6195227..985214be 100644 --- a/src/datavisualization/engine/shaders/surfaceShadowNoTex.frag +++ b/src/datavisualization/engine/shaders/surfaceShadowNoTex.frag @@ -49,7 +49,7 @@ void main() { highp float cosAlpha = clamp(dot(E, R), 0.0, 1.0); highp float bias = 0.005 * tan(acos(cosTheta)); - bias = clamp(bias, 0.0, 0.01); + bias = clamp(bias, 0.001, 0.01); vec4 shadCoords = shadowCoord; shadCoords.z -= bias; diff --git a/src/datavisualization/engine/shaders/surfaceTexturedShadow.frag b/src/datavisualization/engine/shaders/surfaceTexturedShadow.frag new file mode 100644 index 00000000..a4259565 --- /dev/null +++ b/src/datavisualization/engine/shaders/surfaceTexturedShadow.frag @@ -0,0 +1,67 @@ +#version 120 + +uniform highp float lightStrength; +uniform highp float ambientStrength; +uniform highp float shadowQuality; +uniform highp sampler2D textureSampler; +uniform highp sampler2DShadow shadowMap; +uniform highp vec4 lightColor; + +varying highp vec4 shadowCoord; +varying highp vec2 UV; +varying highp vec3 position_wrld; +varying highp vec3 normal_cmr; +varying highp vec3 eyeDirection_cmr; +varying highp vec3 lightDirection_cmr; + +highp vec2 poissonDisk[16] = vec2[16](vec2(-0.94201624, -0.39906216), + vec2(0.94558609, -0.76890725), + vec2(-0.094184101, -0.92938870), + vec2(0.34495938, 0.29387760), + vec2(-0.91588581, 0.45771432), + vec2(-0.81544232, -0.87912464), + vec2(-0.38277543, 0.27676845), + vec2(0.97484398, 0.75648379), + vec2(0.44323325, -0.97511554), + vec2(0.53742981, -0.47373420), + vec2(-0.26496911, -0.41893023), + vec2(0.79197514, 0.19090188), + vec2(-0.24188840, 0.99706507), + vec2(-0.81409955, 0.91437590), + vec2(0.19984126, 0.78641367), + vec2(0.14383161, -0.14100790)); + +void main() { + highp vec3 materialDiffuseColor = texture2D(textureSampler, UV).rgb; + highp vec3 materialAmbientColor = lightColor.rgb * ambientStrength * materialDiffuseColor; + highp vec3 materialSpecularColor = lightColor.rgb * 0.2; + + highp vec3 n = normalize(normal_cmr); + highp vec3 l = normalize(lightDirection_cmr); + highp float cosTheta = clamp(dot(n, l), 0.0, 1.0); + + highp vec3 E = normalize(eyeDirection_cmr); + highp vec3 R = reflect(-l, n); + highp float cosAlpha = clamp(dot(E, R), 0.0, 1.0); + + highp float bias = 0.005 * tan(acos(cosTheta)); + bias = clamp(bias, 0.001, 0.01); + + vec4 shadCoords = shadowCoord; + shadCoords.z -= bias; + + highp float visibility = 0.6; + for (int i = 0; i < 15; i++) { + vec4 shadCoordsPD = shadCoords; + shadCoordsPD.x += cos(poissonDisk[i].x) / shadowQuality; + shadCoordsPD.y += sin(poissonDisk[i].y) / shadowQuality; + visibility += 0.025 * shadow2DProj(shadowMap, shadCoordsPD).r; + } + + gl_FragColor.rgb = + (materialAmbientColor + + materialDiffuseColor * lightStrength * cosTheta + + materialSpecularColor * lightStrength * pow(cosAlpha, 10)); + gl_FragColor.a = texture2D(textureSampler, UV).a; + gl_FragColor.rgb = visibility * clamp(gl_FragColor.rgb, 0.0, 1.0); +} diff --git a/src/datavisualization/engine/shaders/surfaceTexturedShadowFlat.frag b/src/datavisualization/engine/shaders/surfaceTexturedShadowFlat.frag index 421fe342..496f4777 100644 --- a/src/datavisualization/engine/shaders/surfaceTexturedShadowFlat.frag +++ b/src/datavisualization/engine/shaders/surfaceTexturedShadowFlat.frag @@ -49,7 +49,7 @@ void main() { highp float cosAlpha = clamp(dot(E, R), 0.0, 1.0); highp float bias = 0.005 * tan(acos(cosTheta)); - bias = clamp(bias, 0.0, 0.01); + bias = clamp(bias, 0.001, 0.01); vec4 shadCoords = shadowCoord; shadCoords.z -= bias; diff --git a/src/datavisualization/engine/surface3drenderer.cpp b/src/datavisualization/engine/surface3drenderer.cpp index e39c986c..743c6fe0 100644 --- a/src/datavisualization/engine/surface3drenderer.cpp +++ b/src/datavisualization/engine/surface3drenderer.cpp @@ -2886,7 +2886,7 @@ void Surface3DRenderer::initShaders(const QString &vertexShader, const QString & m_surfaceSmoothShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexShadow"), QStringLiteral(":/shaders/fragmentSurfaceShadowNoTex")); m_surfaceTexturedSmoothShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexShadow"), - QStringLiteral(":/shaders/fragmentShadow")); + QStringLiteral(":/shaders/fragmentTexturedSurfaceShadow")); } else { m_surfaceSmoothShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertex"), QStringLiteral(":/shaders/fragmentSurface")); -- cgit v1.2.3 From a845ef527b650af3ce95e03ea2670e120f053b36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomi=20Korpip=C3=A4=C3=A4?= Date: Wed, 22 Oct 2014 10:28:48 +0300 Subject: Added C++ autotests for theme Task-number: QTRD-3368 Change-Id: I369d8cad75f02a0cc585edad57c3ddcd8ef221d9 Change-Id: I369d8cad75f02a0cc585edad57c3ddcd8ef221d9 Reviewed-by: Miikka Heikkinen --- tests/auto/cpptest/cpptest.pro | 3 +- tests/auto/cpptest/q3dtheme/q3dtheme.pro | 8 ++ tests/auto/cpptest/q3dtheme/tst_theme.cpp | 216 ++++++++++++++++++++++++++++++ 3 files changed, 226 insertions(+), 1 deletion(-) create mode 100644 tests/auto/cpptest/q3dtheme/q3dtheme.pro create mode 100644 tests/auto/cpptest/q3dtheme/tst_theme.cpp diff --git a/tests/auto/cpptest/cpptest.pro b/tests/auto/cpptest/cpptest.pro index 9c8d944a..34fbe4e0 100644 --- a/tests/auto/cpptest/cpptest.pro +++ b/tests/auto/cpptest/cpptest.pro @@ -17,4 +17,5 @@ SUBDIRS = q3dbars \ q3daxis-value \ q3dscene \ q3dscene-camera \ - q3dscene-light + q3dscene-light \ + q3dtheme diff --git a/tests/auto/cpptest/q3dtheme/q3dtheme.pro b/tests/auto/cpptest/q3dtheme/q3dtheme.pro new file mode 100644 index 00000000..30a4802c --- /dev/null +++ b/tests/auto/cpptest/q3dtheme/q3dtheme.pro @@ -0,0 +1,8 @@ +QT += testlib datavisualization + +TARGET = tst_cpptest +CONFIG += console testcase + +TEMPLATE = app + +SOURCES += tst_theme.cpp diff --git a/tests/auto/cpptest/q3dtheme/tst_theme.cpp b/tests/auto/cpptest/q3dtheme/tst_theme.cpp new file mode 100644 index 00000000..35945aef --- /dev/null +++ b/tests/auto/cpptest/q3dtheme/tst_theme.cpp @@ -0,0 +1,216 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +#include + +#include + +using namespace QtDataVisualization; + +class tst_theme: public QObject +{ + Q_OBJECT + +private slots: + void initTestCase(); + void cleanupTestCase(); + void init(); + void cleanup(); + + void construct(); + + void initialProperties(); + void initializeProperties(); + void invalidProperties(); + +private: + Q3DTheme *m_theme; +}; + +void tst_theme::initTestCase() +{ +} + +void tst_theme::cleanupTestCase() +{ +} + +void tst_theme::init() +{ + m_theme = new Q3DTheme(); +} + +void tst_theme::cleanup() +{ + delete m_theme; +} + +void tst_theme::construct() +{ + Q3DTheme *theme = new Q3DTheme(); + QVERIFY(theme); + delete theme; + + theme = new Q3DTheme(Q3DTheme::ThemeEbony); + QVERIFY(theme); + QCOMPARE(theme->ambientLightStrength(), 0.5f); + QCOMPARE(theme->backgroundColor(), QColor(Qt::black)); + QCOMPARE(theme->isBackgroundEnabled(), true); + QCOMPARE(theme->baseColors().length(), 5); + QCOMPARE(theme->baseColors().at(0), QColor(Qt::white)); + QCOMPARE(theme->baseColors().at(4), QColor(QRgb(0x6b6b6b))); + QCOMPARE(theme->baseGradients().length(), 5); + QCOMPARE(theme->baseGradients().at(0).stops().at(1).second, QColor(Qt::white)); + QCOMPARE(theme->baseGradients().at(4).stops().at(1).second, QColor(QRgb(0x6b6b6b))); + QCOMPARE(theme->colorStyle(), Q3DTheme::ColorStyleUniform); + QCOMPARE(theme->font(), QFont("Arial")); + QCOMPARE(theme->isGridEnabled(), true); + QCOMPARE(theme->gridLineColor(), QColor(QRgb(0x35322f))); + QCOMPARE(theme->highlightLightStrength(), 5.0f); + QCOMPARE(theme->labelBackgroundColor(), QColor(0x00, 0x00, 0x00, 0xcd)); + QCOMPARE(theme->isLabelBackgroundEnabled(), true); + QCOMPARE(theme->isLabelBorderEnabled(), false); + QCOMPARE(theme->labelTextColor(), QColor(QRgb(0xaeadac))); + QCOMPARE(theme->lightColor(), QColor(Qt::white)); + QCOMPARE(theme->lightStrength(), 5.0f); + QCOMPARE(theme->multiHighlightColor(), QColor(QRgb(0xd72222))); + QCOMPARE(theme->multiHighlightGradient().stops().at(1).second, QColor(QRgb(0xd72222))); + QCOMPARE(theme->singleHighlightColor(), QColor(QRgb(0xf5dc0d))); + QCOMPARE(theme->singleHighlightGradient().stops().at(1).second, QColor(QRgb(0xf5dc0d))); + QCOMPARE(theme->type(), Q3DTheme::ThemeEbony); + QCOMPARE(theme->windowColor(), QColor(Qt::black)); + delete theme; +} + +void tst_theme::initialProperties() +{ + QVERIFY(m_theme); + + QCOMPARE(m_theme->ambientLightStrength(), 0.25f); + QCOMPARE(m_theme->backgroundColor(), QColor(Qt::black)); + QCOMPARE(m_theme->isBackgroundEnabled(), true); + QCOMPARE(m_theme->baseColors().length(), 1); + QCOMPARE(m_theme->baseColors().at(0), QColor(Qt::black)); + QCOMPARE(m_theme->baseGradients().length(), 1); + QCOMPARE(m_theme->baseGradients().at(0).stops().at(0).second, QColor(Qt::black)); + QCOMPARE(m_theme->baseGradients().at(0).stops().at(1).second, QColor(Qt::white)); + QCOMPARE(m_theme->colorStyle(), Q3DTheme::ColorStyleUniform); + QCOMPARE(m_theme->font(), QFont()); + QCOMPARE(m_theme->isGridEnabled(), true); + QCOMPARE(m_theme->gridLineColor(), QColor(Qt::white)); + QCOMPARE(m_theme->highlightLightStrength(), 7.5f); + QCOMPARE(m_theme->labelBackgroundColor(), QColor(Qt::gray)); + QCOMPARE(m_theme->isLabelBackgroundEnabled(), true); + QCOMPARE(m_theme->isLabelBorderEnabled(), true); + QCOMPARE(m_theme->labelTextColor(), QColor(Qt::white)); + QCOMPARE(m_theme->lightColor(), QColor(Qt::white)); + QCOMPARE(m_theme->lightStrength(), 5.0f); + QCOMPARE(m_theme->multiHighlightColor(), QColor(Qt::blue)); + QCOMPARE(m_theme->multiHighlightGradient().stops(), QLinearGradient().stops()); + QCOMPARE(m_theme->singleHighlightColor(), QColor(Qt::red)); + QCOMPARE(m_theme->singleHighlightGradient().stops(), QLinearGradient().stops()); + QCOMPARE(m_theme->type(), Q3DTheme::ThemeUserDefined); + QCOMPARE(m_theme->windowColor(), QColor(Qt::black)); +} + +void tst_theme::initializeProperties() +{ + QVERIFY(m_theme); + + QLinearGradient gradient1; + QLinearGradient gradient2; + QLinearGradient gradient3; + QLinearGradient gradient4; + + QList basecolors; + basecolors << QColor(Qt::red) << QColor(Qt::blue); + + QList basegradients; + basegradients << gradient1 << gradient2; + + m_theme->setType(Q3DTheme::ThemeQt); // We'll override default values with the following setters + m_theme->setAmbientLightStrength(0.3f); + m_theme->setBackgroundColor(QColor(Qt::red)); + m_theme->setBackgroundEnabled(false); + m_theme->setBaseColors(basecolors); + m_theme->setBaseGradients(basegradients); + m_theme->setColorStyle(Q3DTheme::ColorStyleRangeGradient); + m_theme->setFont(QFont("Arial")); + m_theme->setGridEnabled(false); + m_theme->setGridLineColor(QColor(Qt::green)); + m_theme->setHighlightLightStrength(5.0f); + m_theme->setLabelBackgroundColor(QColor(Qt::gray)); + m_theme->setLabelBackgroundEnabled(false); + m_theme->setLabelBorderEnabled(false); + m_theme->setLabelTextColor(QColor(Qt::cyan)); + m_theme->setLightColor(QColor(Qt::yellow)); + m_theme->setLightStrength(2.5f); + m_theme->setMultiHighlightColor(QColor(Qt::darkBlue)); + m_theme->setMultiHighlightGradient(gradient3); + m_theme->setSingleHighlightColor(QColor(Qt::darkRed)); + m_theme->setSingleHighlightGradient(gradient4); + m_theme->setWindowColor(QColor(Qt::darkYellow)); + + QCOMPARE(m_theme->ambientLightStrength(), 0.3f); + QCOMPARE(m_theme->backgroundColor(), QColor(Qt::red)); + QCOMPARE(m_theme->isBackgroundEnabled(), false); + QCOMPARE(m_theme->baseColors().length(), 2); + QCOMPARE(m_theme->baseColors().at(0), QColor(Qt::red)); + QCOMPARE(m_theme->baseColors().at(1), QColor(Qt::blue)); + QCOMPARE(m_theme->baseGradients().length(), 2); + QCOMPARE(m_theme->baseGradients().at(0), gradient1); + QCOMPARE(m_theme->baseGradients().at(0), gradient2); + QCOMPARE(m_theme->colorStyle(), Q3DTheme::ColorStyleRangeGradient); + QCOMPARE(m_theme->font(), QFont("Arial")); + QCOMPARE(m_theme->isGridEnabled(), false); + QCOMPARE(m_theme->gridLineColor(), QColor(Qt::green)); + QCOMPARE(m_theme->highlightLightStrength(), 5.0f); + QCOMPARE(m_theme->labelBackgroundColor(), QColor(Qt::gray)); + QCOMPARE(m_theme->isLabelBackgroundEnabled(), false); + QCOMPARE(m_theme->isLabelBorderEnabled(), false); + QCOMPARE(m_theme->labelTextColor(), QColor(Qt::cyan)); + QCOMPARE(m_theme->lightColor(), QColor(Qt::yellow)); + QCOMPARE(m_theme->lightStrength(), 2.5f); + QCOMPARE(m_theme->multiHighlightColor(), QColor(Qt::darkBlue)); + QCOMPARE(m_theme->multiHighlightGradient(), gradient3); + QCOMPARE(m_theme->singleHighlightColor(), QColor(Qt::darkRed)); + QCOMPARE(m_theme->singleHighlightGradient(), gradient4); + QCOMPARE(m_theme->type(), Q3DTheme::ThemeQt); + QCOMPARE(m_theme->windowColor(), QColor(Qt::darkYellow)); +} + +void tst_theme::invalidProperties() +{ + m_theme->setAmbientLightStrength(-1.0f); + QCOMPARE(m_theme->ambientLightStrength(), 0.25f); + m_theme->setAmbientLightStrength(1.1f); + QCOMPARE(m_theme->ambientLightStrength(), 0.25f); + + m_theme->setHighlightLightStrength(-1.0f); + QCOMPARE(m_theme->highlightLightStrength(), 7.5f); + m_theme->setHighlightLightStrength(10.1f); + QCOMPARE(m_theme->highlightLightStrength(), 7.5f); + + m_theme->setLightStrength(-1.0f); + QCOMPARE(m_theme->lightStrength(), 5.0f); + m_theme->setLightStrength(10.1f); + QCOMPARE(m_theme->lightStrength(), 5.0f); +} + +QTEST_MAIN(tst_theme) +#include "tst_theme.moc" -- cgit v1.2.3 From 8b40f40c4b51fb5a9e02bf4f2c17658500497eff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomi=20Korpip=C3=A4=C3=A4?= Date: Wed, 22 Oct 2014 11:49:21 +0300 Subject: Added C++ autotests for input Task-number: QTRD-3368 Change-Id: I0ca32bcaf4025cac24ece2e80fab6a2eee3562b0 Change-Id: I0ca32bcaf4025cac24ece2e80fab6a2eee3562b0 Reviewed-by: Miikka Heikkinen Reviewed-by: Mika Salmela --- tests/auto/cpptest/cpptest.pro | 4 +- .../auto/cpptest/q3dinput-touch/q3dinput-touch.pro | 8 ++ tests/auto/cpptest/q3dinput-touch/tst_input.cpp | 106 ++++++++++++++++++++ tests/auto/cpptest/q3dinput/q3dinput.pro | 8 ++ tests/auto/cpptest/q3dinput/tst_input.cpp | 109 +++++++++++++++++++++ 5 files changed, 234 insertions(+), 1 deletion(-) create mode 100644 tests/auto/cpptest/q3dinput-touch/q3dinput-touch.pro create mode 100644 tests/auto/cpptest/q3dinput-touch/tst_input.cpp create mode 100644 tests/auto/cpptest/q3dinput/q3dinput.pro create mode 100644 tests/auto/cpptest/q3dinput/tst_input.cpp diff --git a/tests/auto/cpptest/cpptest.pro b/tests/auto/cpptest/cpptest.pro index 34fbe4e0..b3b9b076 100644 --- a/tests/auto/cpptest/cpptest.pro +++ b/tests/auto/cpptest/cpptest.pro @@ -18,4 +18,6 @@ SUBDIRS = q3dbars \ q3dscene \ q3dscene-camera \ q3dscene-light \ - q3dtheme + q3dtheme \ + q3dinput \ + q3dinput-touch diff --git a/tests/auto/cpptest/q3dinput-touch/q3dinput-touch.pro b/tests/auto/cpptest/q3dinput-touch/q3dinput-touch.pro new file mode 100644 index 00000000..2de48158 --- /dev/null +++ b/tests/auto/cpptest/q3dinput-touch/q3dinput-touch.pro @@ -0,0 +1,8 @@ +QT += testlib datavisualization + +TARGET = tst_cpptest +CONFIG += console testcase + +TEMPLATE = app + +SOURCES += tst_input.cpp diff --git a/tests/auto/cpptest/q3dinput-touch/tst_input.cpp b/tests/auto/cpptest/q3dinput-touch/tst_input.cpp new file mode 100644 index 00000000..53d760ae --- /dev/null +++ b/tests/auto/cpptest/q3dinput-touch/tst_input.cpp @@ -0,0 +1,106 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +#include + +#include + +using namespace QtDataVisualization; + +class tst_input: public QObject +{ + Q_OBJECT + +private slots: + void initTestCase(); + void cleanupTestCase(); + void init(); + void cleanup(); + + void construct(); + + void initialProperties(); + void initializeProperties(); + +private: + QTouch3DInputHandler *m_input; +}; + +void tst_input::initTestCase() +{ +} + +void tst_input::cleanupTestCase() +{ +} + +void tst_input::init() +{ + m_input = new QTouch3DInputHandler(); +} + +void tst_input::cleanup() +{ + delete m_input; +} + +void tst_input::construct() +{ + QTouch3DInputHandler *input = new QTouch3DInputHandler(); + QVERIFY(input); + delete input; +} + +void tst_input::initialProperties() +{ + QVERIFY(m_input); + + // Common (from Q3DInputHandler and QAbstract3DInputHandler) + QCOMPARE(m_input->isRotationEnabled(), true); + QCOMPARE(m_input->isSelectionEnabled(), true); + QCOMPARE(m_input->isZoomAtTargetEnabled(), true); + QCOMPARE(m_input->isZoomEnabled(), true); + QCOMPARE(m_input->inputPosition(), QPoint(0, 0)); + QCOMPARE(m_input->inputView(), QAbstract3DInputHandler::InputViewNone); + QVERIFY(!m_input->scene()); +} + +void tst_input::initializeProperties() +{ + QVERIFY(m_input); + + // Common (from Q3DInputHandler and QAbstract3DInputHandler) + m_input->setRotationEnabled(false); + m_input->setSelectionEnabled(false); + m_input->setZoomAtTargetEnabled(false); + m_input->setZoomEnabled(false); + m_input->setInputPosition(QPoint(100, 100)); + m_input->setInputView(QAbstract3DInputHandler::InputViewOnPrimary); + + QCOMPARE(m_input->isRotationEnabled(), false); + QCOMPARE(m_input->isSelectionEnabled(), false); + QCOMPARE(m_input->isZoomAtTargetEnabled(), false); + QCOMPARE(m_input->isZoomEnabled(), false); + QCOMPARE(m_input->inputPosition(), QPoint(100, 100)); + QCOMPARE(m_input->inputView(), QAbstract3DInputHandler::InputViewOnPrimary); +} + +// TODO: QTRD-3380 (mouse/touch events) + +QTEST_MAIN(tst_input) +#include "tst_input.moc" diff --git a/tests/auto/cpptest/q3dinput/q3dinput.pro b/tests/auto/cpptest/q3dinput/q3dinput.pro new file mode 100644 index 00000000..2de48158 --- /dev/null +++ b/tests/auto/cpptest/q3dinput/q3dinput.pro @@ -0,0 +1,8 @@ +QT += testlib datavisualization + +TARGET = tst_cpptest +CONFIG += console testcase + +TEMPLATE = app + +SOURCES += tst_input.cpp diff --git a/tests/auto/cpptest/q3dinput/tst_input.cpp b/tests/auto/cpptest/q3dinput/tst_input.cpp new file mode 100644 index 00000000..68b2225c --- /dev/null +++ b/tests/auto/cpptest/q3dinput/tst_input.cpp @@ -0,0 +1,109 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +#include + +#include + +using namespace QtDataVisualization; + +class tst_input: public QObject +{ + Q_OBJECT + +private slots: + void initTestCase(); + void cleanupTestCase(); + void init(); + void cleanup(); + + void construct(); + + void initialProperties(); + void initializeProperties(); + +private: + Q3DInputHandler *m_input; +}; + +void tst_input::initTestCase() +{ +} + +void tst_input::cleanupTestCase() +{ +} + +void tst_input::init() +{ + m_input = new Q3DInputHandler(); +} + +void tst_input::cleanup() +{ + delete m_input; +} + +void tst_input::construct() +{ + Q3DInputHandler *input = new Q3DInputHandler(); + QVERIFY(input); + delete input; +} + +void tst_input::initialProperties() +{ + QVERIFY(m_input); + + QCOMPARE(m_input->isRotationEnabled(), true); + QCOMPARE(m_input->isSelectionEnabled(), true); + QCOMPARE(m_input->isZoomAtTargetEnabled(), true); + QCOMPARE(m_input->isZoomEnabled(), true); + + // Common (from QAbstract3DInputHandler) + QCOMPARE(m_input->inputPosition(), QPoint(0, 0)); + QCOMPARE(m_input->inputView(), QAbstract3DInputHandler::InputViewNone); + QVERIFY(!m_input->scene()); +} + +void tst_input::initializeProperties() +{ + QVERIFY(m_input); + + m_input->setRotationEnabled(false); + m_input->setSelectionEnabled(false); + m_input->setZoomAtTargetEnabled(false); + m_input->setZoomEnabled(false); + + QCOMPARE(m_input->isRotationEnabled(), false); + QCOMPARE(m_input->isSelectionEnabled(), false); + QCOMPARE(m_input->isZoomAtTargetEnabled(), false); + QCOMPARE(m_input->isZoomEnabled(), false); + + // Common (from QAbstract3DInputHandler) + m_input->setInputPosition(QPoint(100, 100)); + m_input->setInputView(QAbstract3DInputHandler::InputViewOnPrimary); + + QCOMPARE(m_input->inputPosition(), QPoint(100, 100)); + QCOMPARE(m_input->inputView(), QAbstract3DInputHandler::InputViewOnPrimary); +} + +// TODO: QTRD-3380 (mouse events) + +QTEST_MAIN(tst_input) +#include "tst_input.moc" -- cgit v1.2.3 From 14ba79846a1e8a63df47b374f7808ae2447c7e2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomi=20Korpip=C3=A4=C3=A4?= Date: Thu, 23 Oct 2014 07:51:19 +0300 Subject: Added C++ autotests for custom items, labels and volumes. Task-number: QTRD-3368 Change-Id: Ia6199669b0b70190de5a5d057c596093a051c1a9 Change-Id: Ia6199669b0b70190de5a5d057c596093a051c1a9 Reviewed-by: Miikka Heikkinen --- tests/auto/cpptest/cpptest.pro | 5 +- .../cpptest/q3dcustom-label/q3dcustom-label.pro | 8 + tests/auto/cpptest/q3dcustom-label/tst_custom.cpp | 158 ++++++++++++++++ .../cpptest/q3dcustom-volume/q3dcustom-volume.pro | 8 + tests/auto/cpptest/q3dcustom-volume/tst_custom.cpp | 204 +++++++++++++++++++++ tests/auto/cpptest/q3dcustom/q3dcustom.pro | 8 + tests/auto/cpptest/q3dcustom/tst_custom.cpp | 128 +++++++++++++ 7 files changed, 518 insertions(+), 1 deletion(-) create mode 100644 tests/auto/cpptest/q3dcustom-label/q3dcustom-label.pro create mode 100644 tests/auto/cpptest/q3dcustom-label/tst_custom.cpp create mode 100644 tests/auto/cpptest/q3dcustom-volume/q3dcustom-volume.pro create mode 100644 tests/auto/cpptest/q3dcustom-volume/tst_custom.cpp create mode 100644 tests/auto/cpptest/q3dcustom/q3dcustom.pro create mode 100644 tests/auto/cpptest/q3dcustom/tst_custom.cpp diff --git a/tests/auto/cpptest/cpptest.pro b/tests/auto/cpptest/cpptest.pro index b3b9b076..abd8f38e 100644 --- a/tests/auto/cpptest/cpptest.pro +++ b/tests/auto/cpptest/cpptest.pro @@ -20,4 +20,7 @@ SUBDIRS = q3dbars \ q3dscene-light \ q3dtheme \ q3dinput \ - q3dinput-touch + q3dinput-touch \ + q3dcustom \ + q3dcustom-label \ + q3dcustom-volume diff --git a/tests/auto/cpptest/q3dcustom-label/q3dcustom-label.pro b/tests/auto/cpptest/q3dcustom-label/q3dcustom-label.pro new file mode 100644 index 00000000..af584baa --- /dev/null +++ b/tests/auto/cpptest/q3dcustom-label/q3dcustom-label.pro @@ -0,0 +1,8 @@ +QT += testlib datavisualization + +TARGET = tst_cpptest +CONFIG += console testcase + +TEMPLATE = app + +SOURCES += tst_custom.cpp diff --git a/tests/auto/cpptest/q3dcustom-label/tst_custom.cpp b/tests/auto/cpptest/q3dcustom-label/tst_custom.cpp new file mode 100644 index 00000000..40bd5eac --- /dev/null +++ b/tests/auto/cpptest/q3dcustom-label/tst_custom.cpp @@ -0,0 +1,158 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +#include + +#include + +using namespace QtDataVisualization; + +class tst_custom: public QObject +{ + Q_OBJECT + +private slots: + void initTestCase(); + void cleanupTestCase(); + void init(); + void cleanup(); + + void construct(); + + void initialProperties(); + void initializeProperties(); + void invalidProperties(); + +private: + QCustom3DLabel *m_custom; +}; + +void tst_custom::initTestCase() +{ +} + +void tst_custom::cleanupTestCase() +{ +} + +void tst_custom::init() +{ + m_custom = new QCustom3DLabel(); +} + +void tst_custom::cleanup() +{ + delete m_custom; +} + +void tst_custom::construct() +{ + QCustom3DLabel *custom = new QCustom3DLabel(); + QVERIFY(custom); + delete custom; + + custom = new QCustom3DLabel("label", QFont("Times New Roman", 10.0), QVector3D(1.0, 1.0, 1.0), + QVector3D(1.0, 1.0, 1.0), QQuaternion(1.0, 1.0, 10.0, 100.0)); + QVERIFY(custom); + QCOMPARE(custom->backgroundColor(), QColor(Qt::gray)); + QCOMPARE(custom->isBackgroundEnabled(), true); + QCOMPARE(custom->isBorderEnabled(), true); + QCOMPARE(custom->isFacingCamera(), false); + QCOMPARE(custom->font(), QFont("Times New Roman", 10)); + QCOMPARE(custom->text(), QString("label")); + QCOMPARE(custom->textColor(), QColor(Qt::white)); + QCOMPARE(custom->meshFile(), QString(":/defaultMeshes/plane")); + QCOMPARE(custom->position(), QVector3D(1.0, 1.0, 1.0)); + QCOMPARE(custom->isPositionAbsolute(), false); + QCOMPARE(custom->rotation(), QQuaternion(1.0, 1.0, 10.0, 100.0)); + QCOMPARE(custom->scaling(), QVector3D(1.0, 1.0, 1.0)); + QCOMPARE(custom->isScalingAbsolute(), true); + QCOMPARE(custom->isShadowCasting(), false); + QCOMPARE(custom->textureFile(), QString()); + QCOMPARE(custom->isVisible(), true); + delete custom; +} + +void tst_custom::initialProperties() +{ + QVERIFY(m_custom); + + QCOMPARE(m_custom->backgroundColor(), QColor(Qt::gray)); + QCOMPARE(m_custom->isBackgroundEnabled(), true); + QCOMPARE(m_custom->isBorderEnabled(), true); + QCOMPARE(m_custom->isFacingCamera(), false); + QCOMPARE(m_custom->font(), QFont("Arial", 20)); + QCOMPARE(m_custom->text(), QString()); + QCOMPARE(m_custom->textColor(), QColor(Qt::white)); + + // Common (from QCustom3DItem) + QCOMPARE(m_custom->meshFile(), QString(":/defaultMeshes/plane")); + QCOMPARE(m_custom->position(), QVector3D()); + QCOMPARE(m_custom->isPositionAbsolute(), false); + QCOMPARE(m_custom->rotation(), QQuaternion()); + QCOMPARE(m_custom->scaling(), QVector3D(0.1f, 0.1f, 0.1f)); + QCOMPARE(m_custom->isScalingAbsolute(), true); + QCOMPARE(m_custom->isShadowCasting(), false); + QCOMPARE(m_custom->textureFile(), QString()); + QCOMPARE(m_custom->isVisible(), true); +} + +void tst_custom::initializeProperties() +{ + QVERIFY(m_custom); + + m_custom->setBackgroundColor(QColor(Qt::red)); + m_custom->setBackgroundEnabled(false); + m_custom->setBorderEnabled(false); + m_custom->setFacingCamera(true); + m_custom->setFont(QFont("Times New Roman", 10)); + m_custom->setText(QString("This is a Custom Label")); + m_custom->setTextColor(QColor(Qt::blue)); + + QCOMPARE(m_custom->backgroundColor(), QColor(Qt::red)); + QCOMPARE(m_custom->isBackgroundEnabled(), false); + QCOMPARE(m_custom->isBorderEnabled(), false); + QCOMPARE(m_custom->isFacingCamera(), true); + QCOMPARE(m_custom->font(), QFont("Times New Roman", 10)); + QCOMPARE(m_custom->text(), QString("This is a Custom Label")); + QCOMPARE(m_custom->textColor(), QColor(Qt::blue)); + + // Common (from QCustom3DItem) + m_custom->setPosition(QVector3D(1.0, 1.0, 1.0)); + m_custom->setPositionAbsolute(true); + m_custom->setRotation(QQuaternion(1.0, 1.0, 10.0, 100.0)); + m_custom->setScaling(QVector3D(1.0, 1.0, 1.0)); + m_custom->setShadowCasting(true); + m_custom->setVisible(false); + + QCOMPARE(m_custom->position(), QVector3D(1.0, 1.0, 1.0)); + QCOMPARE(m_custom->isPositionAbsolute(), true); + QCOMPARE(m_custom->rotation(), QQuaternion(1.0, 1.0, 10.0, 100.0)); + QCOMPARE(m_custom->scaling(), QVector3D(1.0, 1.0, 1.0)); + QCOMPARE(m_custom->isShadowCasting(), true); + QCOMPARE(m_custom->isVisible(), false); +} + +void tst_custom::invalidProperties() +{ + m_custom->setScalingAbsolute(false); + QCOMPARE(m_custom->isScalingAbsolute(), true); +} + +QTEST_MAIN(tst_custom) +#include "tst_custom.moc" diff --git a/tests/auto/cpptest/q3dcustom-volume/q3dcustom-volume.pro b/tests/auto/cpptest/q3dcustom-volume/q3dcustom-volume.pro new file mode 100644 index 00000000..af584baa --- /dev/null +++ b/tests/auto/cpptest/q3dcustom-volume/q3dcustom-volume.pro @@ -0,0 +1,8 @@ +QT += testlib datavisualization + +TARGET = tst_cpptest +CONFIG += console testcase + +TEMPLATE = app + +SOURCES += tst_custom.cpp diff --git a/tests/auto/cpptest/q3dcustom-volume/tst_custom.cpp b/tests/auto/cpptest/q3dcustom-volume/tst_custom.cpp new file mode 100644 index 00000000..372e8ecf --- /dev/null +++ b/tests/auto/cpptest/q3dcustom-volume/tst_custom.cpp @@ -0,0 +1,204 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +#include + +#include + +using namespace QtDataVisualization; + +class tst_custom: public QObject +{ + Q_OBJECT + +private slots: + void initTestCase(); + void cleanupTestCase(); + void init(); + void cleanup(); + + void construct(); + + void initialProperties(); + void initializeProperties(); + void invalidProperties(); + +private: + QCustom3DVolume *m_custom; +}; + +void tst_custom::initTestCase() +{ +} + +void tst_custom::cleanupTestCase() +{ +} + +void tst_custom::init() +{ + m_custom = new QCustom3DVolume(); +} + +void tst_custom::cleanup() +{ + delete m_custom; +} + +void tst_custom::construct() +{ + QCustom3DVolume *custom = new QCustom3DVolume(); + QVERIFY(custom); + delete custom; + + QVector *tdata = new QVector(1000); + + QVector table; + table << QRgb(0xff00ff) << QRgb(0x00ff00); + + custom = new QCustom3DVolume(QVector3D(1.0, 1.0, 1.0), QVector3D(1.0, 1.0, 1.0), + QQuaternion(1.0, 1.0, 10.0, 100.0), 10, 10, 10, + tdata, QImage::Format_ARGB32, table); + QVERIFY(custom); + QCOMPARE(custom->alphaMultiplier(), 1.0f); + QCOMPARE(custom->drawSliceFrames(), false); + QCOMPARE(custom->drawSliceFrames(), false); + QCOMPARE(custom->preserveOpacity(), true); + QCOMPARE(custom->sliceFrameColor(), QColor(Qt::black)); + QCOMPARE(custom->sliceFrameGaps(), QVector3D(0.01f, 0.01f, 0.01f)); + QCOMPARE(custom->sliceFrameThicknesses(), QVector3D(0.01f, 0.01f, 0.01f)); + QCOMPARE(custom->sliceFrameWidths(), QVector3D(0.01f, 0.01f, 0.01f)); + QCOMPARE(custom->sliceIndexX(), -1); + QCOMPARE(custom->sliceIndexY(), -1); + QCOMPARE(custom->sliceIndexZ(), -1); + QCOMPARE(custom->useHighDefShader(), true); + QCOMPARE(custom->textureData()->length(), 1000); + QCOMPARE(custom->textureDataWidth(), 40); + QCOMPARE(custom->textureFormat(), QImage::Format_ARGB32); + QCOMPARE(custom->textureHeight(), 10); + QCOMPARE(custom->textureWidth(), 10); + QCOMPARE(custom->textureDepth(), 10); + QCOMPARE(custom->meshFile(), QString(":/defaultMeshes/barFull")); + QCOMPARE(custom->position(), QVector3D(1.0, 1.0, 1.0)); + QCOMPARE(custom->isPositionAbsolute(), false); + QCOMPARE(custom->rotation(), QQuaternion(1.0, 1.0, 10.0, 100.0)); + QCOMPARE(custom->scaling(), QVector3D(1.0, 1.0, 1.0)); + QCOMPARE(custom->isScalingAbsolute(), true); + QCOMPARE(custom->isShadowCasting(), false); + QCOMPARE(custom->textureFile(), QString()); + QCOMPARE(custom->isVisible(), true); + delete custom; +} + +void tst_custom::initialProperties() +{ + QVERIFY(m_custom); + + QCOMPARE(m_custom->alphaMultiplier(), 1.0f); + QCOMPARE(m_custom->drawSliceFrames(), false); + QCOMPARE(m_custom->drawSliceFrames(), false); + QCOMPARE(m_custom->preserveOpacity(), true); + QCOMPARE(m_custom->sliceFrameColor(), QColor(Qt::black)); + QCOMPARE(m_custom->sliceFrameGaps(), QVector3D(0.01f, 0.01f, 0.01f)); + QCOMPARE(m_custom->sliceFrameThicknesses(), QVector3D(0.01f, 0.01f, 0.01f)); + QCOMPARE(m_custom->sliceFrameWidths(), QVector3D(0.01f, 0.01f, 0.01f)); + QCOMPARE(m_custom->sliceIndexX(), -1); + QCOMPARE(m_custom->sliceIndexY(), -1); + QCOMPARE(m_custom->sliceIndexZ(), -1); + QCOMPARE(m_custom->useHighDefShader(), true); + + // Common (from QCustom3DVolume) + QCOMPARE(m_custom->meshFile(), QString(":/defaultMeshes/barFull")); + QCOMPARE(m_custom->position(), QVector3D()); + QCOMPARE(m_custom->isPositionAbsolute(), false); + QCOMPARE(m_custom->rotation(), QQuaternion()); + QCOMPARE(m_custom->scaling(), QVector3D(0.1f, 0.1f, 0.1f)); + QCOMPARE(m_custom->isScalingAbsolute(), true); + QCOMPARE(m_custom->isShadowCasting(), true); + QCOMPARE(m_custom->textureFile(), QString()); + QCOMPARE(m_custom->isVisible(), true); +} + +void tst_custom::initializeProperties() +{ + QVERIFY(m_custom); + + m_custom->setAlphaMultiplier(0.1f); + m_custom->setDrawSliceFrames(true); + m_custom->setDrawSliceFrames(true); + m_custom->setPreserveOpacity(false); + m_custom->setSliceFrameColor(QColor(Qt::red)); + m_custom->setSliceFrameGaps(QVector3D(2.0f, 2.0f, 2.0f)); + m_custom->setSliceFrameThicknesses(QVector3D(2.0f, 2.0f, 2.0f)); + m_custom->setSliceFrameWidths(QVector3D(2.0f, 2.0f, 2.0f)); + m_custom->setSliceIndexX(0); + m_custom->setSliceIndexY(0); + m_custom->setSliceIndexZ(0); + m_custom->setUseHighDefShader(false); + + QCOMPARE(m_custom->alphaMultiplier(), 0.1f); + QCOMPARE(m_custom->drawSliceFrames(), true); + QCOMPARE(m_custom->drawSliceFrames(), true); + QCOMPARE(m_custom->preserveOpacity(), false); + QCOMPARE(m_custom->sliceFrameColor(), QColor(Qt::red)); + QCOMPARE(m_custom->sliceFrameGaps(), QVector3D(2.0f, 2.0f, 2.0f)); + QCOMPARE(m_custom->sliceFrameThicknesses(), QVector3D(2.0f, 2.0f, 2.0f)); + QCOMPARE(m_custom->sliceFrameWidths(), QVector3D(2.0f, 2.0f, 2.0f)); + QCOMPARE(m_custom->sliceIndexX(), 0); + QCOMPARE(m_custom->sliceIndexY(), 0); + QCOMPARE(m_custom->sliceIndexZ(), 0); + QCOMPARE(m_custom->useHighDefShader(), false); + + // Common (from QCustom3DVolume) + m_custom->setPosition(QVector3D(1.0, 1.0, 1.0)); + m_custom->setPositionAbsolute(true); + m_custom->setRotation(QQuaternion(1.0, 1.0, 10.0, 100.0)); + m_custom->setScaling(QVector3D(1.0, 1.0, 1.0)); + m_custom->setScalingAbsolute(false); + m_custom->setShadowCasting(false); + m_custom->setVisible(false); + + QCOMPARE(m_custom->position(), QVector3D(1.0, 1.0, 1.0)); + QCOMPARE(m_custom->isPositionAbsolute(), true); + QCOMPARE(m_custom->rotation(), QQuaternion(1.0, 1.0, 10.0, 100.0)); + QCOMPARE(m_custom->scaling(), QVector3D(1.0, 1.0, 1.0)); + QCOMPARE(m_custom->isScalingAbsolute(), false); + QCOMPARE(m_custom->isShadowCasting(), false); + QCOMPARE(m_custom->isVisible(), false); +} + +void tst_custom::invalidProperties() +{ + m_custom->setAlphaMultiplier(-1.0f); + QCOMPARE(m_custom->alphaMultiplier(), 1.0f); + + m_custom->setSliceFrameGaps(QVector3D(-0.1f, -0.1f, -0.1f)); + QCOMPARE(m_custom->sliceFrameGaps(), QVector3D(0.01f, 0.01f, 0.01f)); + + m_custom->setSliceFrameThicknesses(QVector3D(-0.1f, -0.1f, -0.1f)); + QCOMPARE(m_custom->sliceFrameThicknesses(), QVector3D(0.01f, 0.01f, 0.01f)); + + m_custom->setSliceFrameWidths(QVector3D(-0.1f, -0.1f, -0.1f)); + QCOMPARE(m_custom->sliceFrameWidths(), QVector3D(0.01f, 0.01f, 0.01f)); + + m_custom->setTextureFormat(QImage::Format_ARGB8555_Premultiplied); + QCOMPARE(m_custom->textureFormat(), QImage::Format_ARGB32); +} + +QTEST_MAIN(tst_custom) +#include "tst_custom.moc" diff --git a/tests/auto/cpptest/q3dcustom/q3dcustom.pro b/tests/auto/cpptest/q3dcustom/q3dcustom.pro new file mode 100644 index 00000000..af584baa --- /dev/null +++ b/tests/auto/cpptest/q3dcustom/q3dcustom.pro @@ -0,0 +1,8 @@ +QT += testlib datavisualization + +TARGET = tst_cpptest +CONFIG += console testcase + +TEMPLATE = app + +SOURCES += tst_custom.cpp diff --git a/tests/auto/cpptest/q3dcustom/tst_custom.cpp b/tests/auto/cpptest/q3dcustom/tst_custom.cpp new file mode 100644 index 00000000..abc088f9 --- /dev/null +++ b/tests/auto/cpptest/q3dcustom/tst_custom.cpp @@ -0,0 +1,128 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +#include + +#include + +using namespace QtDataVisualization; + +class tst_custom: public QObject +{ + Q_OBJECT + +private slots: + void initTestCase(); + void cleanupTestCase(); + void init(); + void cleanup(); + + void construct(); + + void initialProperties(); + void initializeProperties(); + +private: + QCustom3DItem *m_custom; +}; + +void tst_custom::initTestCase() +{ +} + +void tst_custom::cleanupTestCase() +{ +} + +void tst_custom::init() +{ + m_custom = new QCustom3DItem(); +} + +void tst_custom::cleanup() +{ + delete m_custom; +} + +void tst_custom::construct() +{ + QCustom3DItem *custom = new QCustom3DItem(); + QVERIFY(custom); + delete custom; + + custom = new QCustom3DItem(":/customitem.obj", QVector3D(1.0, 1.0, 1.0), + QVector3D(1.0, 1.0, 1.0), QQuaternion(1.0, 1.0, 10.0, 100.0), + QImage(":/customtexture.jpg")); + QVERIFY(custom); + QCOMPARE(custom->meshFile(), QString(":/customitem.obj")); + QCOMPARE(custom->position(), QVector3D(1.0, 1.0, 1.0)); + QCOMPARE(custom->isPositionAbsolute(), false); + QCOMPARE(custom->rotation(), QQuaternion(1.0, 1.0, 10.0, 100.0)); + QCOMPARE(custom->scaling(), QVector3D(1.0, 1.0, 1.0)); + QCOMPARE(custom->isScalingAbsolute(), true); + QCOMPARE(custom->isShadowCasting(), true); + QCOMPARE(custom->textureFile(), QString()); + QCOMPARE(custom->isVisible(), true); + delete custom; +} + +void tst_custom::initialProperties() +{ + QVERIFY(m_custom); + + QCOMPARE(m_custom->meshFile(), QString()); + QCOMPARE(m_custom->position(), QVector3D()); + QCOMPARE(m_custom->isPositionAbsolute(), false); + QCOMPARE(m_custom->rotation(), QQuaternion()); + QCOMPARE(m_custom->scaling(), QVector3D(0.1f, 0.1f, 0.1f)); + QCOMPARE(m_custom->isScalingAbsolute(), true); + QCOMPARE(m_custom->isShadowCasting(), true); + QCOMPARE(m_custom->textureFile(), QString()); + QCOMPARE(m_custom->isVisible(), true); +} + +void tst_custom::initializeProperties() +{ + QVERIFY(m_custom); + + m_custom->setMeshFile(":/customitem.obj"); + m_custom->setPosition(QVector3D(1.0, 1.0, 1.0)); + m_custom->setPositionAbsolute(true); + m_custom->setRotation(QQuaternion(1.0, 1.0, 10.0, 100.0)); + m_custom->setScaling(QVector3D(1.0, 1.0, 1.0)); + m_custom->setScalingAbsolute(false); + m_custom->setShadowCasting(false); + m_custom->setTextureFile(":/customtexture.jpg"); + m_custom->setVisible(false); + + QCOMPARE(m_custom->meshFile(), QString(":/customitem.obj")); + QCOMPARE(m_custom->position(), QVector3D(1.0, 1.0, 1.0)); + QCOMPARE(m_custom->isPositionAbsolute(), true); + QCOMPARE(m_custom->rotation(), QQuaternion(1.0, 1.0, 10.0, 100.0)); + QCOMPARE(m_custom->scaling(), QVector3D(1.0, 1.0, 1.0)); + QCOMPARE(m_custom->isScalingAbsolute(), false); + QCOMPARE(m_custom->isShadowCasting(), false); + QCOMPARE(m_custom->textureFile(), QString(":/customtexture.jpg")); + QCOMPARE(m_custom->isVisible(), false); + + m_custom->setTextureImage(QImage(QSize(10, 10), QImage::Format_ARGB32)); + QCOMPARE(m_custom->textureFile(), QString()); +} + +QTEST_MAIN(tst_custom) +#include "tst_custom.moc" -- cgit v1.2.3 From 0679c92560c13b97a23dc7d9c70a54f669e3de18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomi=20Korpip=C3=A4=C3=A4?= Date: Thu, 23 Oct 2014 11:35:02 +0300 Subject: Autotests ES2 fix Some properties have different default values, or cannot be changed at all on ES2. Change-Id: Ieb3951f5692a645eafab492819ce18de19134335 Change-Id: Ieb3951f5692a645eafab492819ce18de19134335 Reviewed-by: Miikka Heikkinen --- tests/auto/qmltest/bars3d/tst_basic.qml | 21 +++++++++++++++------ tests/auto/qmltest/scatter3d/tst_basic.qml | 21 +++++++++++++++------ tests/auto/qmltest/surface3d/tst_basic.qml | 21 +++++++++++++++------ 3 files changed, 45 insertions(+), 18 deletions(-) diff --git a/tests/auto/qmltest/bars3d/tst_basic.qml b/tests/auto/qmltest/bars3d/tst_basic.qml index cfba9b4a..bf27ae8c 100644 --- a/tests/auto/qmltest/bars3d/tst_basic.qml +++ b/tests/auto/qmltest/bars3d/tst_basic.qml @@ -131,8 +131,10 @@ Item { function test_1_common() { compare(common.selectionMode, AbstractGraph3D.SelectionItem, "selectionMode") compare(common.shadowQuality, AbstractGraph3D.ShadowQualityMedium, "shadowQuality") - compare(common.shadowsSupported, true, "shadowsSupported") - compare(common.msaaSamples, 4, "msaaSamples") + if (common.shadowsSupported === true) + compare(common.msaaSamples, 4, "msaaSamples") + else + compare(common.msaaSamples, 0, "msaaSamples") compare(common.theme.type, Theme3D.ThemeQt, "theme") compare(common.renderingMode, AbstractGraph3D.RenderIndirect, "renderingMode") compare(common.measureFps, false, "measureFps") @@ -156,7 +158,10 @@ Item { common.shadowQuality = AbstractGraph3D.ShadowQualitySoftHigh compare(common.shadowQuality, AbstractGraph3D.ShadowQualitySoftHigh, "shadowQuality") common.msaaSamples = 8 - compare(common.msaaSamples, 8, "msaaSamples") + if (common.shadowsSupported === true) + compare(common.msaaSamples, 8, "msaaSamples") + else + compare(common.msaaSamples, 0, "msaaSamples") common.theme.type = Theme3D.ThemeRetro common.renderingMode = AbstractGraph3D.RenderDirectToBackground_NoClear common.measureFps = true @@ -209,9 +214,13 @@ Item { function test_4_common_initialized() { compare(common_init.selectionMode, AbstractGraph3D.SelectionNone, "selectionMode") - compare(common_init.shadowQuality, AbstractGraph3D.ShadowQualityLow, "shadowQuality") - compare(common_init.shadowsSupported, true, "shadowsSupported") - compare(common_init.msaaSamples, 2, "msaaSamples") + if (common_init.shadowsSupported === true) { + compare(common_init.shadowQuality, AbstractGraph3D.ShadowQualityLow, "shadowQuality") + compare(common_init.msaaSamples, 2, "msaaSamples") + } else { + compare(common_init.shadowQuality, AbstractGraph3D.ShadowQualityNone, "shadowQuality") + compare(common_init.msaaSamples, 0, "msaaSamples") + } compare(common_init.theme.type, Theme3D.ThemeUserDefined, "theme") compare(common_init.renderingMode, AbstractGraph3D.RenderIndirect, "renderingMode") compare(common_init.measureFps, true, "measureFps") diff --git a/tests/auto/qmltest/scatter3d/tst_basic.qml b/tests/auto/qmltest/scatter3d/tst_basic.qml index 985236be..b7221701 100644 --- a/tests/auto/qmltest/scatter3d/tst_basic.qml +++ b/tests/auto/qmltest/scatter3d/tst_basic.qml @@ -94,8 +94,10 @@ Item { function test_1_common() { compare(common.selectionMode, AbstractGraph3D.SelectionItem, "selectionMode") compare(common.shadowQuality, AbstractGraph3D.ShadowQualityMedium, "shadowQuality") - compare(common.shadowsSupported, true, "shadowsSupported") - compare(common.msaaSamples, 4, "msaaSamples") + if (common.shadowsSupported === true) + compare(common.msaaSamples, 4, "msaaSamples") + else + compare(common.msaaSamples, 0, "msaaSamples") compare(common.theme.type, Theme3D.ThemeQt, "theme") compare(common.renderingMode, AbstractGraph3D.RenderIndirect, "renderingMode") compare(common.measureFps, false, "measureFps") @@ -119,7 +121,10 @@ Item { common.shadowQuality = AbstractGraph3D.ShadowQualitySoftHigh compare(common.shadowQuality, AbstractGraph3D.ShadowQualitySoftHigh, "shadowQuality") common.msaaSamples = 8 - compare(common.msaaSamples, 8, "msaaSamples") + if (common.shadowsSupported === true) + compare(common.msaaSamples, 8, "msaaSamples") + else + compare(common.msaaSamples, 0, "msaaSamples") common.theme.type = Theme3D.ThemeRetro common.renderingMode = AbstractGraph3D.RenderDirectToBackground_NoClear common.measureFps = true @@ -172,9 +177,13 @@ Item { function test_4_common_initialized() { compare(common_init.selectionMode, AbstractGraph3D.SelectionNone, "selectionMode") - compare(common_init.shadowQuality, AbstractGraph3D.ShadowQualityLow, "shadowQuality") - compare(common_init.shadowsSupported, true, "shadowsSupported") - compare(common_init.msaaSamples, 2, "msaaSamples") + if (common_init.shadowsSupported === true) { + compare(common_init.shadowQuality, AbstractGraph3D.ShadowQualityLow, "shadowQuality") + compare(common_init.msaaSamples, 2, "msaaSamples") + } else { + compare(common_init.shadowQuality, AbstractGraph3D.ShadowQualityNone, "shadowQuality") + compare(common_init.msaaSamples, 0, "msaaSamples") + } compare(common_init.theme.type, Theme3D.ThemeUserDefined, "theme") compare(common_init.renderingMode, AbstractGraph3D.RenderIndirect, "renderingMode") compare(common_init.measureFps, true, "measureFps") diff --git a/tests/auto/qmltest/surface3d/tst_basic.qml b/tests/auto/qmltest/surface3d/tst_basic.qml index ba82a524..dfcc4542 100644 --- a/tests/auto/qmltest/surface3d/tst_basic.qml +++ b/tests/auto/qmltest/surface3d/tst_basic.qml @@ -102,8 +102,10 @@ Item { function test_1_common() { compare(common.selectionMode, AbstractGraph3D.SelectionItem, "selectionMode") compare(common.shadowQuality, AbstractGraph3D.ShadowQualityMedium, "shadowQuality") - compare(common.shadowsSupported, true, "shadowsSupported") - compare(common.msaaSamples, 4, "msaaSamples") + if (common.shadowsSupported === true) + compare(common.msaaSamples, 4, "msaaSamples") + else + compare(common.msaaSamples, 0, "msaaSamples") compare(common.theme.type, Theme3D.ThemeQt, "theme") compare(common.renderingMode, AbstractGraph3D.RenderIndirect, "renderingMode") compare(common.measureFps, false, "measureFps") @@ -127,7 +129,10 @@ Item { common.shadowQuality = AbstractGraph3D.ShadowQualitySoftHigh compare(common.shadowQuality, AbstractGraph3D.ShadowQualitySoftHigh, "shadowQuality") common.msaaSamples = 8 - compare(common.msaaSamples, 8, "msaaSamples") + if (common.shadowsSupported === true) + compare(common.msaaSamples, 8, "msaaSamples") + else + compare(common.msaaSamples, 0, "msaaSamples") common.theme.type = Theme3D.ThemeRetro common.renderingMode = AbstractGraph3D.RenderDirectToBackground_NoClear common.measureFps = true @@ -180,9 +185,13 @@ Item { function test_4_common_initialized() { compare(common_init.selectionMode, AbstractGraph3D.SelectionNone, "selectionMode") - compare(common_init.shadowQuality, AbstractGraph3D.ShadowQualityLow, "shadowQuality") - compare(common_init.shadowsSupported, true, "shadowsSupported") - compare(common_init.msaaSamples, 2, "msaaSamples") + if (common_init.shadowsSupported === true) { + compare(common_init.shadowQuality, AbstractGraph3D.ShadowQualityLow, "shadowQuality") + compare(common_init.msaaSamples, 2, "msaaSamples") + } else { + compare(common_init.shadowQuality, AbstractGraph3D.ShadowQualityNone, "shadowQuality") + compare(common_init.msaaSamples, 0, "msaaSamples") + } compare(common_init.theme.type, Theme3D.ThemeUserDefined, "theme") compare(common_init.renderingMode, AbstractGraph3D.RenderIndirect, "renderingMode") compare(common_init.measureFps, true, "measureFps") -- cgit v1.2.3 From 9418882d1eb31c06f8e65e8bd6b6f0e9409d8553 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Thu, 23 Oct 2014 14:45:59 +0300 Subject: Release housekeeping MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Changed the version number to 1.2 where relevant. - Regenerated plugins.qmltypes - Updated known issues Change-Id: I5f737a970d0ac7fc14dbd2d30a8684ecced45ac0 Reviewed-by: Tomi Korpipää --- .qmake.conf | 2 +- README | 14 +- dist/changes-1.2.0 | 2 + qtdatavisualization.pro | 2 +- .../doc/qtdatavisualization.qdocconf | 10 +- .../doc/snippets/doc_src_qmldatavisualization.cpp | 8 +- .../doc/src/qtdatavisualization.qdoc | 15 +- .../global/qdatavisualizationglobal.h | 4 +- .../designer/default/Bars3D.qml | 2 +- .../designer/default/Scatter3D.qml | 2 +- .../designer/default/Surface3D.qml | 2 +- src/datavisualizationqml2/plugins.qmltypes | 334 +++++++++++++++++---- 12 files changed, 317 insertions(+), 80 deletions(-) diff --git a/.qmake.conf b/.qmake.conf index 4ac38ae2..48e733ed 100644 --- a/.qmake.conf +++ b/.qmake.conf @@ -1,6 +1,6 @@ load(qt_build_config) CONFIG += qt_example_installs -MODULE_VERSION=1.1.0 +MODULE_VERSION=1.2.0 CMAKE_MODULE_TESTS=- diff --git a/README b/README index 357507d5..2c548feb 100644 --- a/README +++ b/README @@ -1,5 +1,5 @@ --------------------------- -Qt Data Visualization 1.1.0 +Qt Data Visualization 1.2.0 --------------------------- Qt Data Visualization module provides multiple graph types to visualize data in 3D space @@ -78,12 +78,14 @@ Known Issues so only the Qt Quick 2 versions of graphs are available in practice for those platforms. - Shadows are not supported with OpenGL ES2 (including Angle builds in Windows). - Anti-aliasing doesn't work with OpenGL ES2 (including Angle builds in Windows). +- QCustom3DVolume items are not supported with OpenGL ES2 (including Angle builds in Windows). - Surfaces with non-straight rows and columns do not always render properly. - Q3DLight class (and Light3D QML item) are currently not usable for anything. -- Changing any of Q3DScene properties affecting subviewports currently has no effect. +- Changing most of Q3DScene properties affecting subviewports currently has no effect. - Widget based examples layout incorrectly in iOS. - Reparenting a graph to an item in another QQuickWindow is not supported. -- There is a low-impact binary break between 1.0 and 1.1. The break is due to a QML type - registration conflict with QAbstractItemModel between QtDataVisualization and - QtCommercial.Charts. Introducing the binary break makes it possible to use both - Charts and Data Visualization in the same QML application. +- Android builds of QML applications importing QtDataVisualization also require + "QT += datavisualization" in the pro file. This is because Qt Data Visualization QML plugin has + a dependency to Qt Data Visualization C++ library, which Qt Creator doesn't automatically add + to the deployment package. + diff --git a/dist/changes-1.2.0 b/dist/changes-1.2.0 index cd0f6ec4..b23d83b1 100644 --- a/dist/changes-1.2.0 +++ b/dist/changes-1.2.0 @@ -62,6 +62,8 @@ General: - Fixed the OpenGL context cleanup upon renderer destruction. - Fixed scatter item autosizing when adding a new series. - Fixed a crash related to selection render buffer reuse. +- Fixed the flipped Z-coordinate for absolutely positioned custom items. +- Fixed shadows when viewing the graph directly from above or below. New examples ------------ diff --git a/qtdatavisualization.pro b/qtdatavisualization.pro index bb98435e..faadadbc 100644 --- a/qtdatavisualization.pro +++ b/qtdatavisualization.pro @@ -8,4 +8,4 @@ contains(QT_CONFIG, opengles1) { error(QtDataVisualization does not support OpenGL ES 1!) } -OTHER_FILES += README dist/* +OTHER_FILES += README dist/* .qmake.conf diff --git a/src/datavisualization/doc/qtdatavisualization.qdocconf b/src/datavisualization/doc/qtdatavisualization.qdocconf index 6c4b44d9..4d63b526 100644 --- a/src/datavisualization/doc/qtdatavisualization.qdocconf +++ b/src/datavisualization/doc/qtdatavisualization.qdocconf @@ -6,7 +6,7 @@ include($QT_INSTALL_DOCS/global/qt-html-templates-offline.qdocconf) project = QtDataVisualization description = Qt Data Visualization Reference Documentation -version = 1.1.0 +version = 1.2.0 exampledirs += ../../../examples/datavisualization \ snippets @@ -27,14 +27,14 @@ indexes += $QT_INSTALL_DOCS/qtcore/qtcore.index \ qhp.projects = QtDataVisualization qhp.QtDataVisualization.file = qtdatavisualization.qhp -qhp.QtDataVisualization.namespace = com.digia.qtdatavisualization.110 +qhp.QtDataVisualization.namespace = com.digia.qtdatavisualization.120 qhp.QtDataVisualization.virtualFolder = qtdatavisualization qhp.QtDataVisualization.indexTitle = Qt Data Visualization qhp.QtDataVisualization.indexRoot = -qhp.QtDataVisualization.filterAttributes = qtdatavisualization 1.1.0 qtrefdoc -qhp.QtDataVisualization.customFilters.Qt.name = QtDataVisualization 1.1.0 -qhp.QtDataVisualization.customFilters.Qt.filterAttributes = qtdatavisualization 1.1.0 +qhp.QtDataVisualization.filterAttributes = qtdatavisualization 1.2.0 qtrefdoc +qhp.QtDataVisualization.customFilters.Qt.name = QtDataVisualization 1.2.0 +qhp.QtDataVisualization.customFilters.Qt.filterAttributes = qtdatavisualization 1.2.0 qhp.QtDataVisualization.subprojects = gettingstarted examples classes types qhp.QtDataVisualization.subprojects.gettingstarted.title = Getting Started qhp.QtDataVisualization.subprojects.gettingstarted.indexTitle = Qt Data Visualization Getting Started diff --git a/src/datavisualization/doc/snippets/doc_src_qmldatavisualization.cpp b/src/datavisualization/doc/snippets/doc_src_qmldatavisualization.cpp index 56bfc5ee..1ca3f597 100644 --- a/src/datavisualization/doc/snippets/doc_src_qmldatavisualization.cpp +++ b/src/datavisualization/doc/snippets/doc_src_qmldatavisualization.cpp @@ -17,12 +17,12 @@ ****************************************************************************/ //! [0] -import QtDataVisualization 1.1 +import QtDataVisualization 1.2 //! [0] //! [1] import QtQuick 2.0 -import QtDataVisualization 1.1 +import QtDataVisualization 1.2 Item { width: 640 @@ -61,7 +61,7 @@ Item { //! [2] import QtQuick 2.0 -import QtDataVisualization 1.1 +import QtDataVisualization 1.2 Item { width: 640 @@ -94,7 +94,7 @@ Item { //! [3] import QtQuick 2.0 -import QtDataVisualization 1.1 +import QtDataVisualization 1.2 Item { width: 640 diff --git a/src/datavisualization/doc/src/qtdatavisualization.qdoc b/src/datavisualization/doc/src/qtdatavisualization.qdoc index ad53ded3..b07074b1 100644 --- a/src/datavisualization/doc/src/qtdatavisualization.qdoc +++ b/src/datavisualization/doc/src/qtdatavisualization.qdoc @@ -37,7 +37,7 @@ */ /*! - \qmlmodule QtDataVisualization 1.1 + \qmlmodule QtDataVisualization 1.2 \title Qt Data Visualization QML Types \ingroup qmlmodules @@ -327,16 +327,17 @@ so only the Qt Quick 2 graphs are available in practice for those platforms. \li Shadows are not supported with OpenGL ES2 (including Angle builds in Windows). \li Anti-aliasing doesn't work with OpenGL ES2 (including Angle builds in Windows). - \li QCustom3DVolume items are not supported with OpenGL ES2 (including Angle builds in Windows). + \li QCustom3DVolume items are not supported with OpenGL ES2 (including Angle builds in + Windows). \li Surfaces with non-straight rows and columns do not always render properly. \li Q3DLight class (and Light3D QML item) are currently not usable for anything. - \li Changing any of Q3DScene properties affecting subviewports currently has no effect. + \li Changing most of Q3DScene properties affecting subviewports currently has no effect. \li Widget based examples layout incorrectly in iOS. \li Reparenting a graph to an item in another QQuickWindow is not supported. - \li There is a low-impact binary break between 1.0 and 1.1. The break is due to a QML type - registration conflict with QAbstractItemModel between QtDataVisualization and - QtCommercial.Charts. Introducing the binary break makes it possible to use both - Charts and Data Visualization in the same QML application. + \li Android builds of QML applications importing QtDataVisualization also require + "QT += datavisualization" in the pro file. This is because Qt Data Visualization + QML plugin has a dependency to Qt Data Visualization C++ library, which Qt Creator + doesn't automatically add to the deployment package. \endlist */ diff --git a/src/datavisualization/global/qdatavisualizationglobal.h b/src/datavisualization/global/qdatavisualizationglobal.h index e84d03c0..a7f96361 100644 --- a/src/datavisualization/global/qdatavisualizationglobal.h +++ b/src/datavisualization/global/qdatavisualizationglobal.h @@ -21,11 +21,11 @@ #include -#define QT_DATAVISUALIZATION_VERSION_STR "1.1.0" +#define QT_DATAVISUALIZATION_VERSION_STR "1.2.0" /* QT_DATAVISUALIZATION_VERSION is (major << 16) + (minor << 8) + patch. */ -#define QT_DATAVISUALIZATION_VERSION 0x010100 +#define QT_DATAVISUALIZATION_VERSION 0x010200 /* can be used like #if (QT_DATAVISUALIZATION_VERSION >= QT_DATAVISUALIZATION_VERSION_CHECK(1, 0, 0)) */ diff --git a/src/datavisualizationqml2/designer/default/Bars3D.qml b/src/datavisualizationqml2/designer/default/Bars3D.qml index 10fefe53..c85c0e94 100644 --- a/src/datavisualizationqml2/designer/default/Bars3D.qml +++ b/src/datavisualizationqml2/designer/default/Bars3D.qml @@ -17,7 +17,7 @@ ****************************************************************************/ import QtQuick 2.0 -import QtDataVisualization 1.1 +import QtDataVisualization 1.2 Bars3D { width: 300 diff --git a/src/datavisualizationqml2/designer/default/Scatter3D.qml b/src/datavisualizationqml2/designer/default/Scatter3D.qml index b08d4e24..0bd6cac2 100644 --- a/src/datavisualizationqml2/designer/default/Scatter3D.qml +++ b/src/datavisualizationqml2/designer/default/Scatter3D.qml @@ -17,7 +17,7 @@ ****************************************************************************/ import QtQuick 2.0 -import QtDataVisualization 1.1 +import QtDataVisualization 1.2 Scatter3D { width: 300 diff --git a/src/datavisualizationqml2/designer/default/Surface3D.qml b/src/datavisualizationqml2/designer/default/Surface3D.qml index 77ee476e..f9de62a1 100644 --- a/src/datavisualizationqml2/designer/default/Surface3D.qml +++ b/src/datavisualizationqml2/designer/default/Surface3D.qml @@ -17,7 +17,7 @@ ****************************************************************************/ import QtQuick 2.0 -import QtDataVisualization 1.1 +import QtDataVisualization 1.2 Surface3D { width: 300 diff --git a/src/datavisualizationqml2/plugins.qmltypes b/src/datavisualizationqml2/plugins.qmltypes index 6a580536..956100ed 100644 --- a/src/datavisualizationqml2/plugins.qmltypes +++ b/src/datavisualizationqml2/plugins.qmltypes @@ -4,7 +4,7 @@ import QtQuick.tooling 1.1 // It is used for QML tooling purposes only. // // This file was auto-generated by: -// 'qmlplugindump -nonrelocatable QtDataVisualization 1.1' +// 'qmlplugindump.exe -nonrelocatable QtDataVisualization 1.2' Module { Component { @@ -13,10 +13,11 @@ Module { prototype: "QQuickItem" exports: [ "QtDataVisualization/AbstractGraph3D 1.0", - "QtDataVisualization/AbstractGraph3D 1.1" + "QtDataVisualization/AbstractGraph3D 1.1", + "QtDataVisualization/AbstractGraph3D 1.2" ] isCreatable: false - exportMetaObjectRevisions: [0, 1] + exportMetaObjectRevisions: [0, 1, 2] Enum { name: "SelectionFlag" values: { @@ -113,6 +114,14 @@ Module { Property { name: "selectedElement"; revision: 1; type: "ElementType"; isReadonly: true } Property { name: "aspectRatio"; revision: 1; type: "double" } Property { name: "optimizationHints"; revision: 1; type: "OptimizationHints" } + Property { name: "polar"; revision: 2; type: "bool" } + Property { name: "radialLabelOffset"; revision: 2; type: "float" } + Property { name: "horizontalAspectRatio"; revision: 2; type: "double" } + Property { name: "reflection"; revision: 2; type: "bool" } + Property { name: "reflectivity"; revision: 2; type: "double" } + Property { name: "locale"; revision: 2; type: "QLocale" } + Property { name: "queriedGraphPosition"; revision: 2; type: "QVector3D"; isReadonly: true } + Property { name: "margin"; revision: 2; type: "double" } Signal { name: "selectionModeChanged" Parameter { name: "mode"; type: "AbstractDeclarative::SelectionFlags" } @@ -175,6 +184,46 @@ Module { revision: 1 Parameter { name: "hints"; type: "AbstractDeclarative::OptimizationHints" } } + Signal { + name: "polarChanged" + revision: 2 + Parameter { name: "enabled"; type: "bool" } + } + Signal { + name: "radialLabelOffsetChanged" + revision: 2 + Parameter { name: "offset"; type: "float" } + } + Signal { + name: "horizontalAspectRatioChanged" + revision: 2 + Parameter { name: "ratio"; type: "double" } + } + Signal { + name: "reflectionChanged" + revision: 2 + Parameter { name: "enabled"; type: "bool" } + } + Signal { + name: "reflectivityChanged" + revision: 2 + Parameter { name: "reflectivity"; type: "double" } + } + Signal { + name: "localeChanged" + revision: 2 + Parameter { name: "locale"; type: "QLocale" } + } + Signal { + name: "queriedGraphPositionChanged" + revision: 2 + Parameter { name: "data"; type: "QVector3D" } + } + Signal { + name: "marginChanged" + revision: 2 + Parameter { name: "margin"; type: "double" } + } Method { name: "handleAxisXChanged" Parameter { name: "axis"; type: "QAbstract3DAxis"; isPointer: true } @@ -191,6 +240,7 @@ Module { name: "windowDestroyed" Parameter { name: "obj"; type: "QObject"; isPointer: true } } + Method { name: "destroyContext" } Method { name: "clearSelection" } Method { name: "addCustomItem" @@ -247,9 +297,12 @@ Module { Component { name: "QtDataVisualization::Declarative3DScene" prototype: "QtDataVisualization::Q3DScene" - exports: ["QtDataVisualization/Scene3D 1.0"] + exports: [ + "QtDataVisualization/Scene3D 1.0", + "QtDataVisualization/Scene3D 1.2" + ] isCreatable: false - exportMetaObjectRevisions: [0] + exportMetaObjectRevisions: [0, 1] Property { name: "selectionQueryPosition"; type: "QPointF" } Property { name: "invalidSelectionPoint"; type: "QPoint"; isReadonly: true } Signal { @@ -293,18 +346,22 @@ Module { name: "QtDataVisualization::DeclarativeBars" defaultProperty: "seriesList" prototype: "QtDataVisualization::AbstractDeclarative" - exports: ["QtDataVisualization/Bars3D 1.0"] - exportMetaObjectRevisions: [0] + exports: [ + "QtDataVisualization/Bars3D 1.0", + "QtDataVisualization/Bars3D 1.2" + ] + exportMetaObjectRevisions: [0, 1] Property { name: "rowAxis"; type: "QCategory3DAxis"; isPointer: true } Property { name: "valueAxis"; type: "QValue3DAxis"; isPointer: true } Property { name: "columnAxis"; type: "QCategory3DAxis"; isPointer: true } Property { name: "multiSeriesUniform"; type: "bool" } - Property { name: "barThickness"; type: "double" } + Property { name: "barThickness"; type: "float" } Property { name: "barSpacing"; type: "QSizeF" } Property { name: "barSpacingRelative"; type: "bool" } Property { name: "seriesList"; type: "QBar3DSeries"; isList: true; isReadonly: true } Property { name: "selectedSeries"; type: "QBar3DSeries"; isReadonly: true; isPointer: true } Property { name: "primarySeries"; type: "QBar3DSeries"; isPointer: true } + Property { name: "floorLevel"; revision: 1; type: "float" } Signal { name: "rowAxisChanged" Parameter { name: "axis"; type: "QCategory3DAxis"; isPointer: true } @@ -323,7 +380,7 @@ Module { } Signal { name: "barThicknessChanged" - Parameter { name: "thicknessRatio"; type: "double" } + Parameter { name: "thicknessRatio"; type: "float" } } Signal { name: "barSpacingChanged" @@ -345,6 +402,11 @@ Module { name: "selectedSeriesChanged" Parameter { name: "series"; type: "QBar3DSeries"; isPointer: true } } + Signal { + name: "floorLevelChanged" + revision: 1 + Parameter { name: "level"; type: "float" } + } Method { name: "handleAxisXChanged" Parameter { name: "axis"; type: "QAbstract3DAxis"; isPointer: true } @@ -461,13 +523,17 @@ Module { name: "QtDataVisualization::DeclarativeSurface" defaultProperty: "seriesList" prototype: "QtDataVisualization::AbstractDeclarative" - exports: ["QtDataVisualization/Surface3D 1.0"] - exportMetaObjectRevisions: [0] + exports: [ + "QtDataVisualization/Surface3D 1.0", + "QtDataVisualization/Surface3D 1.2" + ] + exportMetaObjectRevisions: [0, 1] Property { name: "axisX"; type: "QValue3DAxis"; isPointer: true } Property { name: "axisY"; type: "QValue3DAxis"; isPointer: true } Property { name: "axisZ"; type: "QValue3DAxis"; isPointer: true } Property { name: "selectedSeries"; type: "QSurface3DSeries"; isReadonly: true; isPointer: true } Property { name: "seriesList"; type: "QSurface3DSeries"; isList: true; isReadonly: true } + Property { name: "flipHorizontalGrid"; revision: 1; type: "bool" } Signal { name: "axisXChanged" Parameter { name: "axis"; type: "QValue3DAxis"; isPointer: true } @@ -484,6 +550,11 @@ Module { name: "selectedSeriesChanged" Parameter { name: "series"; type: "QSurface3DSeries"; isPointer: true } } + Signal { + name: "flipHorizontalGridChanged" + revision: 1 + Parameter { name: "flip"; type: "bool" } + } Method { name: "handleAxisXChanged" Parameter { name: "axis"; type: "QAbstract3DAxis"; isPointer: true } @@ -560,8 +631,11 @@ Module { Component { name: "QtDataVisualization::Q3DCamera" prototype: "QtDataVisualization::Q3DObject" - exports: ["QtDataVisualization/Camera3D 1.0"] - exportMetaObjectRevisions: [0] + exports: [ + "QtDataVisualization/Camera3D 1.0", + "QtDataVisualization/Camera3D 1.2" + ] + exportMetaObjectRevisions: [0, 1] Enum { name: "CameraPreset" values: { @@ -592,23 +666,26 @@ Module { "CameraPresetDirectlyBelow": 23 } } - Property { name: "xRotation"; type: "double" } - Property { name: "yRotation"; type: "double" } - Property { name: "zoomLevel"; type: "double" } + Property { name: "xRotation"; type: "float" } + Property { name: "yRotation"; type: "float" } + Property { name: "zoomLevel"; type: "float" } Property { name: "cameraPreset"; type: "CameraPreset" } Property { name: "wrapXRotation"; type: "bool" } Property { name: "wrapYRotation"; type: "bool" } + Property { name: "target"; revision: 1; type: "QVector3D" } + Property { name: "minZoomLevel"; revision: 1; type: "float" } + Property { name: "maxZoomLevel"; revision: 1; type: "float" } Signal { name: "xRotationChanged" - Parameter { name: "rotation"; type: "double" } + Parameter { name: "rotation"; type: "float" } } Signal { name: "yRotationChanged" - Parameter { name: "rotation"; type: "double" } + Parameter { name: "rotation"; type: "float" } } Signal { name: "zoomLevelChanged" - Parameter { name: "zoomLevel"; type: "double" } + Parameter { name: "zoomLevel"; type: "float" } } Signal { name: "cameraPresetChanged" @@ -622,10 +699,47 @@ Module { name: "wrapYRotationChanged" Parameter { name: "isEnabled"; type: "bool" } } + Signal { + name: "targetChanged" + revision: 1 + Parameter { name: "target"; type: "QVector3D" } + } + Signal { + name: "minZoomLevelChanged" + revision: 1 + Parameter { name: "zoomLevel"; type: "float" } + } + Signal { + name: "maxZoomLevelChanged" + revision: 1 + Parameter { name: "zoomLevel"; type: "float" } + } } Component { name: "QtDataVisualization::Q3DInputHandler" prototype: "QtDataVisualization::QAbstract3DInputHandler" + exports: ["QtDataVisualization/InputHandler3D 1.2"] + exportMetaObjectRevisions: [0] + Property { name: "rotationEnabled"; type: "bool" } + Property { name: "zoomEnabled"; type: "bool" } + Property { name: "selectionEnabled"; type: "bool" } + Property { name: "zoomAtTargetEnabled"; type: "bool" } + Signal { + name: "rotationEnabledChanged" + Parameter { name: "enable"; type: "bool" } + } + Signal { + name: "zoomEnabledChanged" + Parameter { name: "enable"; type: "bool" } + } + Signal { + name: "selectionEnabledChanged" + Parameter { name: "enable"; type: "bool" } + } + Signal { + name: "zoomAtTargetEnabledChanged" + Parameter { name: "enable"; type: "bool" } + } } Component { name: "QtDataVisualization::Q3DLight" @@ -654,7 +768,8 @@ Module { Property { name: "slicingActive"; type: "bool" } Property { name: "activeCamera"; type: "Q3DCamera"; isPointer: true } Property { name: "activeLight"; type: "Q3DLight"; isPointer: true } - Property { name: "devicePixelRatio"; type: "double" } + Property { name: "devicePixelRatio"; type: "float" } + Property { name: "graphPositionQuery"; revision: 1; type: "QPoint" } Signal { name: "viewportChanged" Parameter { name: "viewport"; type: "QRect" } @@ -685,12 +800,17 @@ Module { } Signal { name: "devicePixelRatioChanged" - Parameter { name: "pixelRatio"; type: "double" } + Parameter { name: "pixelRatio"; type: "float" } } Signal { name: "selectionQueryPositionChanged" Parameter { name: "position"; type: "QPoint" } } + Signal { + name: "graphPositionQueryChanged" + revision: 1 + Parameter { name: "position"; type: "QPoint" } + } } Component { name: "QtDataVisualization::Q3DTheme" @@ -733,9 +853,9 @@ Module { Property { name: "baseGradients"; type: "QList" } Property { name: "singleHighlightGradient"; type: "QLinearGradient" } Property { name: "multiHighlightGradient"; type: "QLinearGradient" } - Property { name: "lightStrength"; type: "double" } - Property { name: "ambientLightStrength"; type: "double" } - Property { name: "highlightLightStrength"; type: "double" } + Property { name: "lightStrength"; type: "float" } + Property { name: "ambientLightStrength"; type: "float" } + Property { name: "highlightLightStrength"; type: "float" } Property { name: "labelBorderEnabled"; type: "bool" } Property { name: "font"; type: "QFont" } Property { name: "backgroundEnabled"; type: "bool" } @@ -796,15 +916,15 @@ Module { } Signal { name: "lightStrengthChanged" - Parameter { name: "strength"; type: "double" } + Parameter { name: "strength"; type: "float" } } Signal { name: "ambientLightStrengthChanged" - Parameter { name: "strength"; type: "double" } + Parameter { name: "strength"; type: "float" } } Signal { name: "highlightLightStrengthChanged" - Parameter { name: "strength"; type: "double" } + Parameter { name: "strength"; type: "float" } } Signal { name: "labelBorderEnabledChanged" @@ -861,10 +981,10 @@ Module { Property { name: "labels"; type: "QStringList" } Property { name: "orientation"; type: "AxisOrientation"; isReadonly: true } Property { name: "type"; type: "AxisType"; isReadonly: true } - Property { name: "min"; type: "double" } - Property { name: "max"; type: "double" } + Property { name: "min"; type: "float" } + Property { name: "max"; type: "float" } Property { name: "autoAdjustRange"; type: "bool" } - Property { name: "labelAutoRotation"; revision: 1; type: "double" } + Property { name: "labelAutoRotation"; revision: 1; type: "float" } Property { name: "titleVisible"; revision: 1; type: "bool" } Property { name: "titleFixed"; revision: 1; type: "bool" } Signal { @@ -877,16 +997,16 @@ Module { } Signal { name: "minChanged" - Parameter { name: "value"; type: "double" } + Parameter { name: "value"; type: "float" } } Signal { name: "maxChanged" - Parameter { name: "value"; type: "double" } + Parameter { name: "value"; type: "float" } } Signal { name: "rangeChanged" - Parameter { name: "min"; type: "double" } - Parameter { name: "max"; type: "double" } + Parameter { name: "min"; type: "float" } + Parameter { name: "max"; type: "float" } } Signal { name: "autoAdjustRangeChanged" @@ -895,7 +1015,7 @@ Module { Signal { name: "labelAutoRotationChanged" revision: 1 - Parameter { name: "angle"; type: "double" } + Parameter { name: "angle"; type: "float" } } Signal { name: "titleVisibilityChanged" @@ -1059,7 +1179,7 @@ Module { Method { name: "setMeshAxisAndAngle" Parameter { name: "axis"; type: "QVector3D" } - Parameter { name: "angle"; type: "double" } + Parameter { name: "angle"; type: "float" } } } Component { @@ -1087,7 +1207,7 @@ Module { exportMetaObjectRevisions: [0] Property { name: "dataProxy"; type: "QBarDataProxy"; isPointer: true } Property { name: "selectedBar"; type: "QPoint" } - Property { name: "meshAngle"; type: "double" } + Property { name: "meshAngle"; type: "float" } Signal { name: "dataProxyChanged" Parameter { name: "proxy"; type: "QBarDataProxy"; isPointer: true } @@ -1098,7 +1218,7 @@ Module { } Signal { name: "meshAngleChanged" - Parameter { name: "angle"; type: "double" } + Parameter { name: "angle"; type: "float" } } } Component { @@ -1156,8 +1276,11 @@ Module { Component { name: "QtDataVisualization::QCustom3DItem" prototype: "QObject" - exports: ["QtDataVisualization/Custom3DItem 1.1"] - exportMetaObjectRevisions: [0] + exports: [ + "QtDataVisualization/Custom3DItem 1.1", + "QtDataVisualization/Custom3DItem 1.2" + ] + exportMetaObjectRevisions: [0, 1] Property { name: "meshFile"; type: "string" } Property { name: "textureFile"; type: "string" } Property { name: "position"; type: "QVector3D" } @@ -1166,6 +1289,7 @@ Module { Property { name: "rotation"; type: "QQuaternion" } Property { name: "visible"; type: "bool" } Property { name: "shadowCasting"; type: "bool" } + Property { name: "scalingAbsolute"; revision: 1; type: "bool" } Signal { name: "meshFileChanged" Parameter { name: "meshFile"; type: "string" } @@ -1198,10 +1322,15 @@ Module { name: "shadowCastingChanged" Parameter { name: "shadowCasting"; type: "bool" } } + Signal { + name: "scalingAbsoluteChanged" + revision: 1 + Parameter { name: "scalingAbsolute"; type: "bool" } + } Method { name: "setRotationAxisAndAngle" Parameter { name: "axis"; type: "QVector3D" } - Parameter { name: "angle"; type: "double" } + Parameter { name: "angle"; type: "float" } } } Component { @@ -1245,6 +1374,97 @@ Module { Parameter { name: "enabled"; type: "bool" } } } + Component { + name: "QtDataVisualization::QCustom3DVolume" + prototype: "QtDataVisualization::QCustom3DItem" + exports: ["QtDataVisualization/Custom3DVolume 1.2"] + exportMetaObjectRevisions: [0] + Property { name: "textureWidth"; type: "int" } + Property { name: "textureHeight"; type: "int" } + Property { name: "textureDepth"; type: "int" } + Property { name: "sliceIndexX"; type: "int" } + Property { name: "sliceIndexY"; type: "int" } + Property { name: "sliceIndexZ"; type: "int" } + Property { name: "colorTable"; type: "QVector" } + Property { name: "textureData"; type: "QVector"; isPointer: true } + Property { name: "alphaMultiplier"; type: "float" } + Property { name: "preserveOpacity"; type: "bool" } + Property { name: "useHighDefShader"; type: "bool" } + Property { name: "drawSlices"; type: "bool" } + Property { name: "drawSliceFrames"; type: "bool" } + Property { name: "sliceFrameColor"; type: "QColor" } + Property { name: "sliceFrameWidths"; type: "QVector3D" } + Property { name: "sliceFrameGaps"; type: "QVector3D" } + Property { name: "sliceFrameThicknesses"; type: "QVector3D" } + Signal { + name: "textureWidthChanged" + Parameter { name: "value"; type: "int" } + } + Signal { + name: "textureHeightChanged" + Parameter { name: "value"; type: "int" } + } + Signal { + name: "textureDepthChanged" + Parameter { name: "value"; type: "int" } + } + Signal { + name: "sliceIndexXChanged" + Parameter { name: "value"; type: "int" } + } + Signal { + name: "sliceIndexYChanged" + Parameter { name: "value"; type: "int" } + } + Signal { + name: "sliceIndexZChanged" + Parameter { name: "value"; type: "int" } + } + Signal { + name: "textureDataChanged" + Parameter { name: "data"; type: "QVector"; isPointer: true } + } + Signal { + name: "textureFormatChanged" + Parameter { name: "format"; type: "QImage::Format" } + } + Signal { + name: "alphaMultiplierChanged" + Parameter { name: "mult"; type: "float" } + } + Signal { + name: "preserveOpacityChanged" + Parameter { name: "enabled"; type: "bool" } + } + Signal { + name: "useHighDefShaderChanged" + Parameter { name: "enabled"; type: "bool" } + } + Signal { + name: "drawSlicesChanged" + Parameter { name: "enabled"; type: "bool" } + } + Signal { + name: "drawSliceFramesChanged" + Parameter { name: "enabled"; type: "bool" } + } + Signal { + name: "sliceFrameColorChanged" + Parameter { name: "color"; type: "QColor" } + } + Signal { + name: "sliceFrameWidthsChanged" + Parameter { name: "values"; type: "QVector3D" } + } + Signal { + name: "sliceFrameGapsChanged" + Parameter { name: "values"; type: "QVector3D" } + } + Signal { + name: "sliceFrameThicknessesChanged" + Parameter { name: "values"; type: "QVector3D" } + } + } Component { name: "QtDataVisualization::QHeightMapSurfaceDataProxy" prototype: "QtDataVisualization::QSurfaceDataProxy" @@ -1252,10 +1472,10 @@ Module { exportMetaObjectRevisions: [0] Property { name: "heightMap"; type: "QImage" } Property { name: "heightMapFile"; type: "string" } - Property { name: "minXValue"; type: "double" } - Property { name: "maxXValue"; type: "double" } - Property { name: "minZValue"; type: "double" } - Property { name: "maxZValue"; type: "double" } + Property { name: "minXValue"; type: "float" } + Property { name: "maxXValue"; type: "float" } + Property { name: "minZValue"; type: "float" } + Property { name: "maxZValue"; type: "float" } Signal { name: "heightMapChanged" Parameter { name: "image"; type: "QImage" } @@ -1266,19 +1486,19 @@ Module { } Signal { name: "minXValueChanged" - Parameter { name: "value"; type: "double" } + Parameter { name: "value"; type: "float" } } Signal { name: "maxXValueChanged" - Parameter { name: "value"; type: "double" } + Parameter { name: "value"; type: "float" } } Signal { name: "minZValueChanged" - Parameter { name: "value"; type: "double" } + Parameter { name: "value"; type: "float" } } Signal { name: "maxZValueChanged" - Parameter { name: "value"; type: "double" } + Parameter { name: "value"; type: "float" } } } Component { @@ -1657,7 +1877,7 @@ Module { exportMetaObjectRevisions: [0] Property { name: "dataProxy"; type: "QScatterDataProxy"; isPointer: true } Property { name: "selectedItem"; type: "int" } - Property { name: "itemSize"; type: "double" } + Property { name: "itemSize"; type: "float" } Signal { name: "dataProxyChanged" Parameter { name: "proxy"; type: "QScatterDataProxy"; isPointer: true } @@ -1668,7 +1888,7 @@ Module { } Signal { name: "itemSizeChanged" - Parameter { name: "size"; type: "double" } + Parameter { name: "size"; type: "float" } } } Component { @@ -1736,6 +1956,8 @@ Module { Property { name: "flatShadingEnabled"; type: "bool" } Property { name: "flatShadingSupported"; type: "bool"; isReadonly: true } Property { name: "drawMode"; type: "DrawFlags" } + Property { name: "texture"; type: "QImage" } + Property { name: "textureFile"; type: "string" } Signal { name: "dataProxyChanged" Parameter { name: "proxy"; type: "QSurfaceDataProxy"; isPointer: true } @@ -1756,6 +1978,14 @@ Module { name: "drawModeChanged" Parameter { name: "mode"; type: "QSurface3DSeries::DrawFlags" } } + Signal { + name: "textureChanged" + Parameter { name: "image"; type: "QImage" } + } + Signal { + name: "textureFileChanged" + Parameter { name: "filename"; type: "string" } + } } Component { name: "QtDataVisualization::QSurfaceDataProxy" @@ -1808,6 +2038,8 @@ Module { Component { name: "QtDataVisualization::QTouch3DInputHandler" prototype: "QtDataVisualization::Q3DInputHandler" + exports: ["QtDataVisualization/TouchInputHandler3D 1.2"] + exportMetaObjectRevisions: [0] } Component { name: "QtDataVisualization::QValue3DAxis" -- cgit v1.2.3 From 33b3fe378486eddddf63a320ebcc5ebb3db0e0d5 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Fri, 24 Oct 2014 09:50:12 +0300 Subject: Make build check for existence of quick module. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: Icd04b72bb7bdbf34c73e6f27c90674fda4fc4cc2 Reviewed-by: Tomi Korpipää --- examples/datavisualization/datavisualization.pro | 27 ++++++++------ src/src.pro | 5 ++- tests/auto/auto.pro | 6 ++- tests/tests.pro | 47 ++++++++++++++---------- 4 files changed, 49 insertions(+), 36 deletions(-) diff --git a/examples/datavisualization/datavisualization.pro b/examples/datavisualization/datavisualization.pro index eab15b6d..b6ece48d 100644 --- a/examples/datavisualization/datavisualization.pro +++ b/examples/datavisualization/datavisualization.pro @@ -1,15 +1,17 @@ TEMPLATE = subdirs -SUBDIRS += qmlbars \ - qmlscatter \ - qmlsurface \ - qmlcustominput \ - qmllegend \ - qmlmultigraph \ - qmloscilloscope \ - qmlsurfacelayers \ - qmlaxisformatter \ - qmlaxisdrag \ - qmlspectrogram +qtHaveModule(quick) { + SUBDIRS += qmlbars \ + qmlscatter \ + qmlsurface \ + qmlcustominput \ + qmllegend \ + qmlmultigraph \ + qmloscilloscope \ + qmlsurfacelayers \ + qmlaxisformatter \ + qmlaxisdrag \ + qmlspectrogram +} !android:!ios { SUBDIRS += bars \ @@ -23,6 +25,7 @@ SUBDIRS += qmlbars \ customitems \ texturesurface \ volumetric + + qtHaveModule(multimedia): SUBDIRS += audiolevels } -qtHaveModule(multimedia):!android:!ios: SUBDIRS += audiolevels diff --git a/src/src.pro b/src/src.pro index 33e3c009..3547745d 100644 --- a/src/src.pro +++ b/src/src.pro @@ -1,4 +1,5 @@ TEMPLATE = subdirs CONFIG += ordered -SUBDIRS += datavisualization \ - datavisualizationqml2 +SUBDIRS += datavisualization + +qtHaveModule(quick): SUBDIRS += datavisualizationqml2 diff --git a/tests/auto/auto.pro b/tests/auto/auto.pro index 6f70bd1b..df2943a4 100644 --- a/tests/auto/auto.pro +++ b/tests/auto/auto.pro @@ -1,5 +1,7 @@ TEMPLATE = subdirs -SUBDIRS = qmltest \ - cpptest + +SUBDIRS += cpptest + +qtHaveModule(quick): SUBDIRS += qmltest installed_cmake.depends = cmake diff --git a/tests/tests.pro b/tests/tests.pro index 34ddb5e5..ccecd486 100644 --- a/tests/tests.pro +++ b/tests/tests.pro @@ -7,26 +7,33 @@ contains(QT_CONFIG, opengles1) { TEMPLATE = subdirs -SUBDIRS += barstest \ - scattertest \ - surfacetest \ - qmlcamera \ - qmldynamicdata \ - multigraphs \ - directional \ - qmlmultiwindow \ - itemmodeltest \ - qmlmultitest \ - volumetrictest \ - qmlvolume \ - auto \ - qmlperf +SUBDIRS += auto -#SUBDIRS += kinectsurface +qtHaveModule(quick) { + SUBDIRS += qmlcamera \ + qmldynamicdata \ + qmlmultiwindow \ + qmlmultitest \ + qmlvolume \ + qmlperf +} -qtHaveModule(multimedia):!android:!static: SUBDIRS += spectrum +!android:!ios { + SUBDIRS += barstest \ + scattertest \ + surfacetest \ + multigraphs \ + directional \ + itemmodeltest \ + volumetrictest + + # For testing code snippets of minimal applications + SUBDIRS += minimalbars \ + minimalscatter \ + minimalsurface -# For testing code snippets of minimal applications -SUBDIRS += minimalbars \ - minimalscatter \ - minimalsurface + # Requires Kinect drivers + #SUBDIRS += kinectsurface +} + +qtHaveModule(multimedia):!android:!static: SUBDIRS += spectrum -- cgit v1.2.3 From 6c6363af51940da4bc42570c3f749d961b2969f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomi=20Korpipa=CC=88a=CC=88?= Date: Mon, 27 Oct 2014 09:21:36 +0200 Subject: Mac touchpad support added Task-number: QTRD-2286 Change-Id: Ibe211caedab231e908af900af65d4075b926a875 Reviewed-by: Miikka Heikkinen --- src/datavisualization/datavisualization.pro | 3 ++- src/datavisualization/engine/qabstract3dgraph.cpp | 12 ++++++++++++ src/datavisualizationqml2/abstractdeclarative.cpp | 15 +++++++++++++++ src/datavisualizationqml2/datavisualizationqml2.pro | 1 + 4 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/datavisualization/datavisualization.pro b/src/datavisualization/datavisualization.pro index f0e56073..645a1733 100644 --- a/src/datavisualization/datavisualization.pro +++ b/src/datavisualization/datavisualization.pro @@ -7,7 +7,8 @@ mac:CONFIG(shared, static|shared):contains(QT_CONFIG, qt_framework) { } message($$QT_CONFIG) -QT = core gui +QT += core gui +osx: QT += gui-private DEFINES += QT_DATAVISUALIZATION_LIBRARY # Fix exports in static builds for applications linking datavisualization module diff --git a/src/datavisualization/engine/qabstract3dgraph.cpp b/src/datavisualization/engine/qabstract3dgraph.cpp index 47802c89..96c82cb6 100644 --- a/src/datavisualization/engine/qabstract3dgraph.cpp +++ b/src/datavisualization/engine/qabstract3dgraph.cpp @@ -29,6 +29,9 @@ #include #include #include +#if defined(Q_OS_OSX) +#include +#endif QT_BEGIN_NAMESPACE_DATAVISUALIZATION @@ -208,6 +211,15 @@ QAbstract3DGraph::QAbstract3DGraph(QAbstract3DGraphPrivate *d, const QSurfaceFor #endif d_ptr->renderLater(); + +#if defined(Q_OS_OSX) + // Enable touch events for Mac touchpads + typedef void * (*EnableTouch)(QWindow*, bool); + EnableTouch enableTouch = + (EnableTouch)QGuiApplication::platformNativeInterface()->nativeResourceFunctionForIntegration("registertouchwindow"); + if (enableTouch) + enableTouch(this, true); +#endif } /*! diff --git a/src/datavisualizationqml2/abstractdeclarative.cpp b/src/datavisualizationqml2/abstractdeclarative.cpp index 7e8de95c..b668c08f 100644 --- a/src/datavisualizationqml2/abstractdeclarative.cpp +++ b/src/datavisualizationqml2/abstractdeclarative.cpp @@ -24,6 +24,9 @@ #if defined(Q_OS_IOS) #include #endif +#if defined(Q_OS_OSX) +#include +#endif QT_BEGIN_NAMESPACE_DATAVISUALIZATION @@ -443,6 +446,18 @@ void AbstractDeclarative::handleWindowChanged(QQuickWindow *window) if (!window) return; +#if defined(Q_OS_OSX) + bool previousVisibility = window->isVisible(); + // Enable touch events for Mac touchpads + window->setVisible(true); + typedef void * (*EnableTouch)(QWindow*, bool); + EnableTouch enableTouch = + (EnableTouch)QGuiApplication::platformNativeInterface()->nativeResourceFunctionForIntegration("registertouchwindow"); + if (enableTouch) + enableTouch(window, true); + window->setVisible(previousVisibility); +#endif + connect(window, &QObject::destroyed, this, &AbstractDeclarative::windowDestroyed); int oldWindowSamples = m_windowSamples; diff --git a/src/datavisualizationqml2/datavisualizationqml2.pro b/src/datavisualizationqml2/datavisualizationqml2.pro index 7c65d69e..87376e70 100644 --- a/src/datavisualizationqml2/datavisualizationqml2.pro +++ b/src/datavisualizationqml2/datavisualizationqml2.pro @@ -1,5 +1,6 @@ TARGET = datavisualizationqml2 QT += qml quick datavisualization +osx: QT += gui-private TARGETPATH = QtDataVisualization IMPORT_VERSION = $$MODULE_VERSION -- cgit v1.2.3 From 23f2810fdfeed3cf8c8c1d1b280aa0052dcb8fc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomi=20Korpip=C3=A4=C3=A4?= Date: Mon, 27 Oct 2014 11:48:04 +0200 Subject: Updated minimum Qt version to 5.2.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I56bcd9eee06dc3ca6a282e97eefaecfd0d017e6e Change-Id: I56bcd9eee06dc3ca6a282e97eefaecfd0d017e6e Reviewed-by: Tomi Korpipää --- README | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README b/README index 2c548feb..667dcdaa 100644 --- a/README +++ b/README @@ -8,7 +8,7 @@ both with C++ and Qt Quick 2. System Requirements =================== -- Qt 5.2 or newer +- Qt 5.2.1 or newer - OpenGL 2.1 or newer (recommended) or OpenGL ES2 (reduced feature set) - Manipulating Qt Data Visualization graphs with QML Designer requires Qt Creator 3.1 or newer -- cgit v1.2.3 From 440479cc4703c44b9fd59dcf679e426fcdd750d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomi=20Korpip=C3=A4=C3=A4?= Date: Tue, 28 Oct 2014 11:20:41 +0200 Subject: Added v1.2 properties to designer plugin Also added missing properties from v1.1. Task-number: QTRD-3396 Change-Id: Ic001e726dbe09238ab51b1fdfb2a58d99b5e29e2 Reviewed-by: Miikka Heikkinen --- .../designer/Bars3DSpecifics.qml | 86 ++++++++++++++++++++++ .../designer/Scatter3DSpecifics.qml | 72 ++++++++++++++++++ .../designer/Surface3DSpecifics.qml | 70 ++++++++++++++++++ 3 files changed, 228 insertions(+) diff --git a/src/datavisualizationqml2/designer/Bars3DSpecifics.qml b/src/datavisualizationqml2/designer/Bars3DSpecifics.qml index cb5fb4a0..bd32d383 100644 --- a/src/datavisualizationqml2/designer/Bars3DSpecifics.qml +++ b/src/datavisualizationqml2/designer/Bars3DSpecifics.qml @@ -288,6 +288,92 @@ Column { Layout.fillWidth: true } } + Label { + text: qsTr("aspectRatio") + toolTip: qsTr("Aspect Ratio") + Layout.fillWidth: true + } + SecondColumnLayout { + SpinBox { + backendValue: backendValues.aspectRatio + minimumValue: 0.01 + maximumValue: 100.0 + stepSize: 0.01 + decimals: 2 + Layout.fillWidth: true + } + } + Label { + text: qsTr("floorLevel") + toolTip: qsTr("Floor Level") + Layout.fillWidth: true + } + SecondColumnLayout { + LineEdit { + backendValue: backendValues.floorLevel + inputMethodHints: Qt.ImhFormattedNumbersOnly + Layout.fillWidth: true + } + } + Label { + text: qsTr("horizontalAspectRatio") + toolTip: qsTr("Horizontal Aspect Ratio") + Layout.fillWidth: true + } + SecondColumnLayout { + SpinBox { + backendValue: backendValues.horizontalAspectRatio + minimumValue: 0.0 + maximumValue: 100.0 + stepSize: 0.01 + decimals: 2 + Layout.fillWidth: true + } + } + Label { + text: qsTr("reflection") + toolTip: qsTr("Reflection") + Layout.fillWidth: true + } + SecondColumnLayout { + CheckBox { + id: reflectionCheckbox + backendValue: backendValues.reflection + Layout.fillWidth: true + } + } + Label { + text: qsTr("reflectivity") + toolTip: qsTr("Reflectivity") + Layout.fillWidth: true + visible: reflectionCheckbox.checked + } + SecondColumnLayout { + visible: reflectionCheckbox.checked + SpinBox { + backendValue: backendValues.reflectivity + minimumValue: 0.0 + maximumValue: 1.0 + stepSize: 0.01 + decimals: 1 + Layout.fillWidth: true + } + } + Label { + text: qsTr("margin") + toolTip: qsTr("Graph Margin") + Layout.fillWidth: true + } + SecondColumnLayout { + SpinBox { + backendValue: backendValues.margin + minimumValue: -1.0 + maximumValue: 100.0 + stepSize: 0.1 + decimals: 1 + Layout.fillWidth: true + } + } // Kept for debugging Label { } diff --git a/src/datavisualizationqml2/designer/Scatter3DSpecifics.qml b/src/datavisualizationqml2/designer/Scatter3DSpecifics.qml index 1e2556ec..131f71fd 100644 --- a/src/datavisualizationqml2/designer/Scatter3DSpecifics.qml +++ b/src/datavisualizationqml2/designer/Scatter3DSpecifics.qml @@ -121,6 +121,78 @@ Column { Layout.fillWidth: true } } + Label { + text: qsTr("optimizationHints") + toolTip: qsTr("Optimization Hints") + Layout.fillWidth: true + } + SecondColumnLayout { + ComboBox { + backendValue: backendValues.optimizationHints + model: ["OptimizationDefault", "OptimizationStatic"] + Layout.fillWidth: true + scope: "AbstractGraph3D" + } + } + Label { + text: qsTr("polar") + toolTip: qsTr("Use Polar Coordinates") + Layout.fillWidth: true + } + SecondColumnLayout { + CheckBox { + id: polarCheckbox + backendValue: backendValues.polar + Layout.fillWidth: true + } + } + Label { + text: qsTr("radialLabelOffset") + toolTip: qsTr("Radial Label Offset") + Layout.fillWidth: true + visible: polarCheckbox.checked + } + SecondColumnLayout { + visible: polarCheckbox.checked + SpinBox { + backendValue: backendValues.radialLabelOffset + minimumValue: 0.0 + maximumValue: 1.0 + stepSize: 0.01 + decimals: 1 + Layout.fillWidth: true + } + } + Label { + text: qsTr("horizontalAspectRatio") + toolTip: qsTr("Horizontal Aspect Ratio") + Layout.fillWidth: true + } + SecondColumnLayout { + SpinBox { + backendValue: backendValues.horizontalAspectRatio + minimumValue: 0.0 + maximumValue: 100.0 + stepSize: 0.01 + decimals: 2 + Layout.fillWidth: true + } + } + Label { + text: qsTr("margin") + toolTip: qsTr("Graph Margin") + Layout.fillWidth: true + } + SecondColumnLayout { + SpinBox { + backendValue: backendValues.margin + minimumValue: -1.0 + maximumValue: 100.0 + stepSize: 0.1 + decimals: 1 + Layout.fillWidth: true + } + } } } } diff --git a/src/datavisualizationqml2/designer/Surface3DSpecifics.qml b/src/datavisualizationqml2/designer/Surface3DSpecifics.qml index 65a65d37..a834f677 100644 --- a/src/datavisualizationqml2/designer/Surface3DSpecifics.qml +++ b/src/datavisualizationqml2/designer/Surface3DSpecifics.qml @@ -241,6 +241,76 @@ Column { Layout.fillWidth: true } } + Label { + text: qsTr("flipHorizontalGrid") + toolTip: qsTr("Flip Horizontal Grid") + Layout.fillWidth: true + } + SecondColumnLayout { + CheckBox { + backendValue: backendValues.flipHorizontalGrid + Layout.fillWidth: true + } + } + Label { + text: qsTr("polar") + toolTip: qsTr("Use Polar Coordinates") + Layout.fillWidth: true + } + SecondColumnLayout { + CheckBox { + id: polarCheckbox + backendValue: backendValues.polar + Layout.fillWidth: true + } + } + Label { + text: qsTr("radialLabelOffset") + toolTip: qsTr("Radial Label Offset") + Layout.fillWidth: true + visible: polarCheckbox.checked + } + SecondColumnLayout { + visible: polarCheckbox.checked + SpinBox { + backendValue: backendValues.radialLabelOffset + minimumValue: 0.0 + maximumValue: 1.0 + stepSize: 0.01 + decimals: 1 + Layout.fillWidth: true + } + } + Label { + text: qsTr("horizontalAspectRatio") + toolTip: qsTr("Horizontal Aspect Ratio") + Layout.fillWidth: true + } + SecondColumnLayout { + SpinBox { + backendValue: backendValues.horizontalAspectRatio + minimumValue: 0.0 + maximumValue: 100.0 + stepSize: 0.01 + decimals: 2 + Layout.fillWidth: true + } + } + Label { + text: qsTr("margin") + toolTip: qsTr("Graph Margin") + Layout.fillWidth: true + } + SecondColumnLayout { + SpinBox { + backendValue: backendValues.margin + minimumValue: -1.0 + maximumValue: 100.0 + stepSize: 0.1 + decimals: 1 + Layout.fillWidth: true + } + } // Kept for debugging Label { } -- cgit v1.2.3 From 56d9ec81afc59b1cf6aaae7675ff460a6bc664a8 Mon Sep 17 00:00:00 2001 From: Mika Salmela Date: Tue, 28 Oct 2014 12:28:02 +0200 Subject: Squeeze labels on ES2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If the label text with slightly smaller font would fit into smaller texture, force to use the smaller one. Task-number: QTRD-3395 Change-Id: Ib70f6722ea8403e4aa25ed190b21c059d9d0e062 Reviewed-by: Miikka Heikkinen Reviewed-by: Tomi Korpipää --- src/datavisualization/utils/texturehelper.cpp | 5 ++--- src/datavisualization/utils/utils.cpp | 31 +++++++++++++++++++++------ src/datavisualization/utils/utils_p.h | 2 +- 3 files changed, 28 insertions(+), 10 deletions(-) diff --git a/src/datavisualization/utils/texturehelper.cpp b/src/datavisualization/utils/texturehelper.cpp index 2c40eb6a..939ce901 100644 --- a/src/datavisualization/utils/texturehelper.cpp +++ b/src/datavisualization/utils/texturehelper.cpp @@ -59,9 +59,8 @@ GLuint TextureHelper::create2DTexture(const QImage &image, bool useTrilinearFilt QImage texImage = image; #if defined(QT_OPENGL_ES_2) - GLuint temp; - GLuint imageWidth = Utils::getNearestPowerOfTwo(image.width(), temp); - GLuint imageHeight = Utils::getNearestPowerOfTwo(image.height(), temp); + GLuint imageWidth = Utils::getNearestPowerOfTwo(image.width()); + GLuint imageHeight = Utils::getNearestPowerOfTwo(image.height()); if (smoothScale) { texImage = image.scaled(imageWidth, imageHeight, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); diff --git a/src/datavisualization/utils/utils.cpp b/src/datavisualization/utils/utils.cpp index 2767451e..534c79f9 100644 --- a/src/datavisualization/utils/utils.cpp +++ b/src/datavisualization/utils/utils.cpp @@ -27,11 +27,10 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION static GLint maxTextureSize = 0; // Safe, as all instances have the same texture size -GLuint Utils::getNearestPowerOfTwo(GLuint value, GLuint &padding) +GLuint Utils::getNearestPowerOfTwo(GLuint value) { GLuint powOfTwoValue = MIN_POWER; NUM_IN_POWER(powOfTwoValue, value); - padding = powOfTwoValue - value; return powOfTwoValue; } @@ -60,7 +59,9 @@ QImage Utils::printTextToImage(const QFont &font, const QString &text, const QCo glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize); GLuint paddingWidth = 20; +#if !defined(QT_OPENGL_ES_2) // TODO fix ifdef for dynamic OpenGL GLuint paddingHeight = 20; +#endif // Calculate text dimensions QFont valueFont = font; valueFont.setPointSize(textureFontSize); @@ -78,23 +79,41 @@ QImage Utils::printTextToImage(const QFont &font, const QString &text, const QCo QSize labelSize; qreal fontRatio = 1.0; +#if defined(QT_OPENGL_ES_2) // TODO fix ifdef for dynamic OpenGL + // Test if text with slighly smaller font would fit into one step smaller texture + // ie. if the text is just exceeded the smaller texture boundary, it would + // make a label with large empty space + GLuint prePadding = 20; + GLint targetWidth = maxTextureSize; + uint testWidth = getNearestPowerOfTwo(valueStrWidth + prePadding) >> 1; + int diffToFit = (valueStrWidth + prePadding) - testWidth; + int maxSqueeze = int((valueStrWidth + prePadding) * 0.1f); + if (diffToFit < maxSqueeze && maxTextureSize > GLint(testWidth)) + targetWidth = testWidth; +#endif + bool sizeOk = false; int currentFontSize = textureFontSize; do { -#if defined(QT_OPENGL_ES_2) +#if defined(QT_OPENGL_ES_2) // TODO fix ifdef for dynamic OpenGL // ES2 can't handle textures with dimensions not in power of 2. Resize labels accordingly. // Add some padding before converting to power of two to avoid too tight fit - GLuint prePadding = 5; labelSize = QSize(valueStrWidth + prePadding, valueStrHeight + prePadding); - labelSize.setWidth(getNearestPowerOfTwo(labelSize.width(), paddingWidth)); - labelSize.setHeight(getNearestPowerOfTwo(labelSize.height(), paddingHeight)); + labelSize.setWidth(getNearestPowerOfTwo(labelSize.width())); + labelSize.setHeight(getNearestPowerOfTwo(labelSize.height())); #else if (!labelBackground) labelSize = QSize(valueStrWidth, valueStrHeight); else labelSize = QSize(valueStrWidth + paddingWidth * 2, valueStrHeight + paddingHeight * 2); #endif + +#if defined(QT_OPENGL_ES_2) + if (!maxTextureSize || (labelSize.width() <= maxTextureSize + && labelSize.width() <= targetWidth)) { +#else if (!maxTextureSize || labelSize.width() <= maxTextureSize) { +#endif // Make sure the label is not too wide sizeOk = true; } else if (--currentFontSize == 4) { diff --git a/src/datavisualization/utils/utils_p.h b/src/datavisualization/utils/utils_p.h index 805c41a7..fdcb0da0 100644 --- a/src/datavisualization/utils/utils_p.h +++ b/src/datavisualization/utils/utils_p.h @@ -45,7 +45,7 @@ public: ParamTypeReal }; - static GLuint getNearestPowerOfTwo(GLuint value, GLuint &padding); + static GLuint getNearestPowerOfTwo(GLuint value); static QVector4D vectorFromColor(const QColor &color); static QColor colorFromVector(const QVector3D &colorVector); static QColor colorFromVector(const QVector4D &colorVector); -- cgit v1.2.3 From bf716cfdf0afecccdb1f2eabb2e6a172c620fbff Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Tue, 28 Oct 2014 12:48:17 +0200 Subject: Support for dynamic opengl builds. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Task-number: QTRD-3392 Change-Id: I786bbf5ee9252b92d43d02d6b88ed9b986b4a5eb Reviewed-by: Tomi Korpipää --- .../datavisualization/volumetric/volumetric.cpp | 228 ++++---- src/datavisualization/data/labelitem.cpp | 6 +- .../engine/abstract3dcontroller.cpp | 12 +- .../engine/abstract3dcontroller_p.h | 1 + .../engine/abstract3drenderer.cpp | 281 +++++----- .../engine/abstract3drenderer_p.h | 10 +- src/datavisualization/engine/bars3drenderer.cpp | 226 ++++---- src/datavisualization/engine/bars3drenderer_p.h | 2 - src/datavisualization/engine/drawer.cpp | 5 +- src/datavisualization/engine/qabstract3dgraph.cpp | 33 +- src/datavisualization/engine/scatter3drenderer.cpp | 573 ++++++++++----------- src/datavisualization/engine/scatter3drenderer_p.h | 5 - src/datavisualization/engine/selectionpointer.cpp | 17 +- src/datavisualization/engine/seriesrendercache.cpp | 5 +- src/datavisualization/engine/surface3drenderer.cpp | 388 +++++++------- src/datavisualization/engine/surface3drenderer_p.h | 2 - .../utils/abstractobjecthelper.cpp | 1 + src/datavisualization/utils/objecthelper.cpp | 1 - src/datavisualization/utils/qutils.h | 6 +- .../utils/scatterobjectbufferhelper.cpp | 2 - .../utils/scatterpointbufferhelper.cpp | 2 - src/datavisualization/utils/surfaceobject.cpp | 1 - src/datavisualization/utils/texturehelper.cpp | 146 +++--- src/datavisualization/utils/texturehelper_p.h | 6 +- src/datavisualization/utils/utils.cpp | 144 +++--- src/datavisualization/utils/utils_p.h | 1 + src/datavisualizationqml2/abstractdeclarative.cpp | 39 +- tests/barstest/main.cpp | 17 +- tests/scattertest/main.cpp | 3 +- 29 files changed, 1063 insertions(+), 1100 deletions(-) diff --git a/examples/datavisualization/volumetric/volumetric.cpp b/examples/datavisualization/volumetric/volumetric.cpp index 788ada7a..20338598 100644 --- a/examples/datavisualization/volumetric/volumetric.cpp +++ b/examples/datavisualization/volumetric/volumetric.cpp @@ -29,6 +29,7 @@ #include #include #include +#include using namespace QtDataVisualization; @@ -53,6 +54,17 @@ const int waterColorsMin(underWaterGroundColorsMax + 1); const int waterColorsMax(waterColorsMin + layerColorThickness); const int terrainTransparency(12); +static bool isOpenGLES() +{ +#if defined(QT_OPENGL_ES_2) + return true; +#elif (QT_VERSION < QT_VERSION_CHECK(5, 3, 0)) + return false; +#else + return QOpenGLContext::currentContext()->isOpenGLES(); +#endif +} + VolumetricModifier::VolumetricModifier(Q3DScatter *scatter) : m_graph(scatter), m_volumeItem(0), @@ -93,117 +105,117 @@ VolumetricModifier::VolumetricModifier(Q3DScatter *scatter) toggleAreaAll(true); -#if !defined(QT_OPENGL_ES_2) - m_lowDetailData = new QVector(lowDetailSize * lowDetailSize * lowDetailSize / 2); - m_mediumDetailData = new QVector(mediumDetailSize * mediumDetailSize * mediumDetailSize / 2); - m_highDetailData = new QVector(highDetailSize * highDetailSize * highDetailSize / 2); - - initHeightMap(QStringLiteral(":/heightmaps/layer_ground.png"), m_groundLayer); - initHeightMap(QStringLiteral(":/heightmaps/layer_water.png"), m_waterLayer); - initHeightMap(QStringLiteral(":/heightmaps/layer_magma.png"), m_magmaLayer); - - initMineShaftArray(); - - createVolume(lowDetailSize, 0, lowDetailSize, m_lowDetailData); - excavateMineShaft(lowDetailSize, 0, m_mineShaftArray.size(), m_lowDetailData); - - //! [0] - m_volumeItem = new QCustom3DVolume; - // Adjust water level to zero with a minor tweak to y-coordinate position and scaling - m_volumeItem->setScaling( - QVector3D(m_graph->axisX()->max() - m_graph->axisX()->min(), - (m_graph->axisY()->max() - m_graph->axisY()->min()) * 0.91f, - m_graph->axisZ()->max() - m_graph->axisZ()->min())); - m_volumeItem->setPosition( - QVector3D((m_graph->axisX()->max() + m_graph->axisX()->min()) / 2.0f, - -0.045f * (m_graph->axisY()->max() - m_graph->axisY()->min()) + - (m_graph->axisY()->max() + m_graph->axisY()->min()) / 2.0f, - (m_graph->axisZ()->max() + m_graph->axisZ()->min()) / 2.0f)); - m_volumeItem->setScalingAbsolute(false); - //! [0] - //! [1] - m_volumeItem->setTextureWidth(lowDetailSize); - m_volumeItem->setTextureHeight(lowDetailSize / 2); - m_volumeItem->setTextureDepth(lowDetailSize); - m_volumeItem->setTextureFormat(QImage::Format_Indexed8); - m_volumeItem->setTextureData(new QVector(*m_lowDetailData)); - //! [1] - - // Generate color tables. - m_colorTable1.resize(colorTableSize); - m_colorTable2.resize(colorTableSize); - - for (int i = 0; i < colorTableSize - 2; i++) { - if (i < magmaColorsMax) { - m_colorTable1[i] = qRgba(130 - (i * 2), 0, 0, 255); - } else if (i < aboveWaterGroundColorsMax) { - m_colorTable1[i] = qRgba((i - magmaColorsMax) * 4, - ((i - magmaColorsMax) * 2) + 120, - (i - magmaColorsMax) * 5, terrainTransparency); - } else if (i < underWaterGroundColorsMax) { - m_colorTable1[i] = qRgba(((layerColorThickness - i - aboveWaterGroundColorsMax)) + 70, - ((layerColorThickness - i - aboveWaterGroundColorsMax) * 2) + 20, - ((layerColorThickness - i - aboveWaterGroundColorsMax)) + 50, - terrainTransparency); - } else if (i < waterColorsMax) { - m_colorTable1[i] = qRgba(0, 0, ((i - underWaterGroundColorsMax) * 2) + 120, - terrainTransparency); - } else { - m_colorTable1[i] = qRgba(0, 0, 0, 0); // Not used + if (!isOpenGLES()) { + m_lowDetailData = new QVector(lowDetailSize * lowDetailSize * lowDetailSize / 2); + m_mediumDetailData = new QVector(mediumDetailSize * mediumDetailSize * mediumDetailSize / 2); + m_highDetailData = new QVector(highDetailSize * highDetailSize * highDetailSize / 2); + + initHeightMap(QStringLiteral(":/heightmaps/layer_ground.png"), m_groundLayer); + initHeightMap(QStringLiteral(":/heightmaps/layer_water.png"), m_waterLayer); + initHeightMap(QStringLiteral(":/heightmaps/layer_magma.png"), m_magmaLayer); + + initMineShaftArray(); + + createVolume(lowDetailSize, 0, lowDetailSize, m_lowDetailData); + excavateMineShaft(lowDetailSize, 0, m_mineShaftArray.size(), m_lowDetailData); + + //! [0] + m_volumeItem = new QCustom3DVolume; + // Adjust water level to zero with a minor tweak to y-coordinate position and scaling + m_volumeItem->setScaling( + QVector3D(m_graph->axisX()->max() - m_graph->axisX()->min(), + (m_graph->axisY()->max() - m_graph->axisY()->min()) * 0.91f, + m_graph->axisZ()->max() - m_graph->axisZ()->min())); + m_volumeItem->setPosition( + QVector3D((m_graph->axisX()->max() + m_graph->axisX()->min()) / 2.0f, + -0.045f * (m_graph->axisY()->max() - m_graph->axisY()->min()) + + (m_graph->axisY()->max() + m_graph->axisY()->min()) / 2.0f, + (m_graph->axisZ()->max() + m_graph->axisZ()->min()) / 2.0f)); + m_volumeItem->setScalingAbsolute(false); + //! [0] + //! [1] + m_volumeItem->setTextureWidth(lowDetailSize); + m_volumeItem->setTextureHeight(lowDetailSize / 2); + m_volumeItem->setTextureDepth(lowDetailSize); + m_volumeItem->setTextureFormat(QImage::Format_Indexed8); + m_volumeItem->setTextureData(new QVector(*m_lowDetailData)); + //! [1] + + // Generate color tables. + m_colorTable1.resize(colorTableSize); + m_colorTable2.resize(colorTableSize); + + for (int i = 0; i < colorTableSize - 2; i++) { + if (i < magmaColorsMax) { + m_colorTable1[i] = qRgba(130 - (i * 2), 0, 0, 255); + } else if (i < aboveWaterGroundColorsMax) { + m_colorTable1[i] = qRgba((i - magmaColorsMax) * 4, + ((i - magmaColorsMax) * 2) + 120, + (i - magmaColorsMax) * 5, terrainTransparency); + } else if (i < underWaterGroundColorsMax) { + m_colorTable1[i] = qRgba(((layerColorThickness - i - aboveWaterGroundColorsMax)) + 70, + ((layerColorThickness - i - aboveWaterGroundColorsMax) * 2) + 20, + ((layerColorThickness - i - aboveWaterGroundColorsMax)) + 50, + terrainTransparency); + } else if (i < waterColorsMax) { + m_colorTable1[i] = qRgba(0, 0, ((i - underWaterGroundColorsMax) * 2) + 120, + terrainTransparency); + } else { + m_colorTable1[i] = qRgba(0, 0, 0, 0); // Not used + } } - } - m_colorTable1[airColorIndex] = qRgba(0, 0, 0, 0); - m_colorTable1[mineShaftColorIndex] = qRgba(50, 50, 50, 255); - - // The alternate color table just has gray gradients for all terrain except water - for (int i = 0; i < colorTableSize - 2; i++) { - if (i < magmaColorsMax) { - m_colorTable2[i] = qRgba(((i - aboveWaterGroundColorsMax) * 2), - ((i - aboveWaterGroundColorsMax) * 2), - ((i - aboveWaterGroundColorsMax) * 2), 255); - } else if (i < underWaterGroundColorsMax) { - m_colorTable2[i] = qRgba(((i - aboveWaterGroundColorsMax) * 2), - ((i - aboveWaterGroundColorsMax) * 2), - ((i - aboveWaterGroundColorsMax) * 2), terrainTransparency); - } else if (i < waterColorsMax) { - m_colorTable2[i] = qRgba(0, 0, ((i - underWaterGroundColorsMax) * 2) + 120, - terrainTransparency); - } else { - m_colorTable2[i] = qRgba(0, 0, 0, 0); // Not used + m_colorTable1[airColorIndex] = qRgba(0, 0, 0, 0); + m_colorTable1[mineShaftColorIndex] = qRgba(50, 50, 50, 255); + + // The alternate color table just has gray gradients for all terrain except water + for (int i = 0; i < colorTableSize - 2; i++) { + if (i < magmaColorsMax) { + m_colorTable2[i] = qRgba(((i - aboveWaterGroundColorsMax) * 2), + ((i - aboveWaterGroundColorsMax) * 2), + ((i - aboveWaterGroundColorsMax) * 2), 255); + } else if (i < underWaterGroundColorsMax) { + m_colorTable2[i] = qRgba(((i - aboveWaterGroundColorsMax) * 2), + ((i - aboveWaterGroundColorsMax) * 2), + ((i - aboveWaterGroundColorsMax) * 2), terrainTransparency); + } else if (i < waterColorsMax) { + m_colorTable2[i] = qRgba(0, 0, ((i - underWaterGroundColorsMax) * 2) + 120, + terrainTransparency); + } else { + m_colorTable2[i] = qRgba(0, 0, 0, 0); // Not used + } } + m_colorTable2[airColorIndex] = qRgba(0, 0, 0, 0); + m_colorTable2[mineShaftColorIndex] = qRgba(255, 255, 0, 255); + + //! [2] + m_volumeItem->setColorTable(m_colorTable1); + //! [2] + + //! [5] + m_volumeItem->setSliceFrameGaps(QVector3D(0.01f, 0.02f, 0.01f)); + m_volumeItem->setSliceFrameThicknesses(QVector3D(0.0025f, 0.005f, 0.0025f)); + m_volumeItem->setSliceFrameWidths(QVector3D(0.0025f, 0.005f, 0.0025f)); + m_volumeItem->setDrawSliceFrames(false); + //! [5] + handleSlicingChanges(); + + //! [3] + m_graph->addCustomItem(m_volumeItem); + //! [3] + + m_timer.start(0); + } else { + // OpenGL ES2 doesn't support 3D textures, so show a warning label instead + QCustom3DLabel *warningLabel = new QCustom3DLabel( + "QCustom3DVolume is not supported with OpenGL ES2", + QFont(), + QVector3D(0.0f, 0.5f, 0.0f), + QVector3D(1.5f, 1.5f, 0.0f), + QQuaternion()); + warningLabel->setPositionAbsolute(true); + warningLabel->setFacingCamera(true); + m_graph->addCustomItem(warningLabel); } - m_colorTable2[airColorIndex] = qRgba(0, 0, 0, 0); - m_colorTable2[mineShaftColorIndex] = qRgba(255, 255, 0, 255); - - //! [2] - m_volumeItem->setColorTable(m_colorTable1); - //! [2] - - //! [5] - m_volumeItem->setSliceFrameGaps(QVector3D(0.01f, 0.02f, 0.01f)); - m_volumeItem->setSliceFrameThicknesses(QVector3D(0.0025f, 0.005f, 0.0025f)); - m_volumeItem->setSliceFrameWidths(QVector3D(0.0025f, 0.005f, 0.0025f)); - m_volumeItem->setDrawSliceFrames(false); - //! [5] - handleSlicingChanges(); - - //! [3] - m_graph->addCustomItem(m_volumeItem); - //! [3] - - m_timer.start(0); -#else - // OpenGL ES2 doesn't support 3D textures, so show a warning label instead - QCustom3DLabel *warningLabel = new QCustom3DLabel( - "QCustom3DVolume is not supported with OpenGL ES2", - QFont(), - QVector3D(0.0f, 0.5f, 0.0f), - QVector3D(1.5f, 1.5f, 0.0f), - QQuaternion()); - warningLabel->setPositionAbsolute(true); - warningLabel->setFacingCamera(true); - m_graph->addCustomItem(warningLabel); -#endif QObject::connect(m_graph, &QAbstract3DGraph::currentFpsChanged, this, &VolumetricModifier::handleFpsChange); diff --git a/src/datavisualization/data/labelitem.cpp b/src/datavisualization/data/labelitem.cpp index 859b0550..ec8ba3fd 100644 --- a/src/datavisualization/data/labelitem.cpp +++ b/src/datavisualization/data/labelitem.cpp @@ -28,7 +28,7 @@ LabelItem::LabelItem() LabelItem::~LabelItem() { - glDeleteTextures(1, &m_textureId); + QOpenGLContext::currentContext()->functions()->glDeleteTextures(1, &m_textureId); } void LabelItem::setSize(const QSize &size) @@ -43,7 +43,7 @@ QSize LabelItem::size() const void LabelItem::setTextureId(GLuint textureId) { - glDeleteTextures(1, &m_textureId); + QOpenGLContext::currentContext()->functions()->glDeleteTextures(1, &m_textureId); m_textureId = textureId; } @@ -55,7 +55,7 @@ GLuint LabelItem::textureId() const void LabelItem::clear() { if (m_textureId) { - glDeleteTextures(1, &m_textureId); + QOpenGLContext::currentContext()->functions()->glDeleteTextures(1, &m_textureId); m_textureId = 0; } m_size = QSize(0, 0); diff --git a/src/datavisualization/engine/abstract3dcontroller.cpp b/src/datavisualization/engine/abstract3dcontroller.cpp index 37d7c08b..275d0fe2 100644 --- a/src/datavisualization/engine/abstract3dcontroller.cpp +++ b/src/datavisualization/engine/abstract3dcontroller.cpp @@ -25,6 +25,7 @@ #include "thememanager_p.h" #include "q3dtheme_p.h" #include "qcustom3ditem_p.h" +#include "utils_p.h" #include #include @@ -940,11 +941,7 @@ QAbstract3DGraph::OptimizationHints Abstract3DController::optimizationHints() co bool Abstract3DController::shadowsSupported() const { -#if defined(QT_OPENGL_ES_2) - return false; -#else - return true; -#endif + return !isOpenGLES(); } bool Abstract3DController::isSlicingActive() const @@ -1362,6 +1359,11 @@ void Abstract3DController::markSeriesItemLabelsDirty() m_seriesList.at(i)->d_ptr->markItemLabelDirty(); } +bool Abstract3DController::isOpenGLES() const +{ + return Utils::isOpenGLES(); +} + void Abstract3DController::setAxisHelper(QAbstract3DAxis::AxisOrientation orientation, QAbstract3DAxis *axis, QAbstract3DAxis **axisPtr) { diff --git a/src/datavisualization/engine/abstract3dcontroller_p.h b/src/datavisualization/engine/abstract3dcontroller_p.h index 5bb3d863..d5a1ac8f 100644 --- a/src/datavisualization/engine/abstract3dcontroller_p.h +++ b/src/datavisualization/engine/abstract3dcontroller_p.h @@ -345,6 +345,7 @@ public: virtual void adjustAxisRanges() = 0; void markSeriesItemLabelsDirty(); + bool isOpenGLES() const; public slots: void destroyRenderer(); diff --git a/src/datavisualization/engine/abstract3drenderer.cpp b/src/datavisualization/engine/abstract3drenderer.cpp index e4141f4c..cfc691af 100644 --- a/src/datavisualization/engine/abstract3drenderer.cpp +++ b/src/datavisualization/engine/abstract3drenderer.cpp @@ -33,6 +33,9 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION +// Defined in shaderhelper.cpp +extern void discardDebugMsgs(QtMsgType type, const QMessageLogContext &context, const QString &msg); + const qreal doublePi(M_PI * 2.0); const int polarGridRoundness(64); const qreal polarGridAngle(doublePi / qreal(polarGridRoundness)); @@ -104,9 +107,28 @@ Abstract3DRenderer::Abstract3DRenderer(Abstract3DController *controller) m_oldCameraTarget(QVector3D(2000.0f, 2000.0f, 2000.0f)), // Just random invalid target m_reflectionEnabled(false), m_reflectivity(0.5), +#if !defined(QT_OPENGL_ES_2) + m_funcs_2_1(0), +#endif m_context(0), - m_dummySurfaceAtDelete(0) + m_dummySurfaceAtDelete(0), + m_isOpenGLES(true) + { + initializeOpenGLFunctions(); + m_isOpenGLES = Utils::isOpenGLES(); +#if !defined(QT_OPENGL_ES_2) + if (!m_isOpenGLES) { + // Discard warnings about deprecated functions + QtMessageHandler handler = qInstallMessageHandler(discardDebugMsgs); + + m_funcs_2_1 = QOpenGLContext::currentContext()->versionFunctions(); + m_funcs_2_1->initializeOpenGLFunctions(); + + // Restore original message handler + qInstallMessageHandler(handler); + } +#endif QObject::connect(m_drawer, &Drawer::drawerChanged, this, &Abstract3DRenderer::updateTextures); QObject::connect(this, &Abstract3DRenderer::needRender, controller, &Abstract3DController::needRender, Qt::QueuedConnection); @@ -174,9 +196,11 @@ void Abstract3DRenderer::initializeOpenGL() glCullFace(GL_BACK); #if !defined(QT_OPENGL_ES_2) - glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); - glHint(GL_LINE_SMOOTH_HINT, GL_NICEST); - glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST); + if (!m_isOpenGLES) { + glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); + glHint(GL_LINE_SMOOTH_HINT, GL_NICEST); + glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST); + } #endif m_textureHelper = new TextureHelper(); @@ -381,91 +405,89 @@ void Abstract3DRenderer::updateTextures() void Abstract3DRenderer::reInitShaders() { -#if !defined(QT_OPENGL_ES_2) - if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { - if (m_cachedOptimizationHint.testFlag(QAbstract3DGraph::OptimizationStatic) - && qobject_cast(this)) { - initGradientShaders(QStringLiteral(":/shaders/vertexShadow"), - QStringLiteral(":/shaders/fragmentShadow")); - initStaticSelectedItemShaders(QStringLiteral(":/shaders/vertexShadow"), - QStringLiteral(":/shaders/fragmentShadowNoTex"), - QStringLiteral(":/shaders/vertexShadow"), - QStringLiteral(":/shaders/fragmentShadowNoTexColorOnY")); - initShaders(QStringLiteral(":/shaders/vertexShadowNoMatrices"), - QStringLiteral(":/shaders/fragmentShadowNoTex")); + if (!m_isOpenGLES) { + if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { + if (m_cachedOptimizationHint.testFlag(QAbstract3DGraph::OptimizationStatic) + && qobject_cast(this)) { + initGradientShaders(QStringLiteral(":/shaders/vertexShadow"), + QStringLiteral(":/shaders/fragmentShadow")); + initStaticSelectedItemShaders(QStringLiteral(":/shaders/vertexShadow"), + QStringLiteral(":/shaders/fragmentShadowNoTex"), + QStringLiteral(":/shaders/vertexShadow"), + QStringLiteral(":/shaders/fragmentShadowNoTexColorOnY")); + initShaders(QStringLiteral(":/shaders/vertexShadowNoMatrices"), + QStringLiteral(":/shaders/fragmentShadowNoTex")); + } else { + initGradientShaders(QStringLiteral(":/shaders/vertexShadow"), + QStringLiteral(":/shaders/fragmentShadowNoTexColorOnY")); + initShaders(QStringLiteral(":/shaders/vertexShadow"), + QStringLiteral(":/shaders/fragmentShadowNoTex")); + } + initBackgroundShaders(QStringLiteral(":/shaders/vertexShadow"), + QStringLiteral(":/shaders/fragmentShadowNoTex")); + initCustomItemShaders(QStringLiteral(":/shaders/vertexShadow"), + QStringLiteral(":/shaders/fragmentShadow")); } else { - initGradientShaders(QStringLiteral(":/shaders/vertexShadow"), - QStringLiteral(":/shaders/fragmentShadowNoTexColorOnY")); - initShaders(QStringLiteral(":/shaders/vertexShadow"), - QStringLiteral(":/shaders/fragmentShadowNoTex")); + if (m_cachedOptimizationHint.testFlag(QAbstract3DGraph::OptimizationStatic) + && qobject_cast(this)) { + initGradientShaders(QStringLiteral(":/shaders/vertexTexture"), + QStringLiteral(":/shaders/fragmentTexture")); + initStaticSelectedItemShaders(QStringLiteral(":/shaders/vertex"), + QStringLiteral(":/shaders/fragment"), + QStringLiteral(":/shaders/vertex"), + QStringLiteral(":/shaders/fragmentColorOnY")); + initShaders(QStringLiteral(":/shaders/vertexNoMatrices"), + QStringLiteral(":/shaders/fragment")); + } else { + initGradientShaders(QStringLiteral(":/shaders/vertex"), + QStringLiteral(":/shaders/fragmentColorOnY")); + initShaders(QStringLiteral(":/shaders/vertex"), + QStringLiteral(":/shaders/fragment")); + } + initBackgroundShaders(QStringLiteral(":/shaders/vertex"), + QStringLiteral(":/shaders/fragment")); + initCustomItemShaders(QStringLiteral(":/shaders/vertexTexture"), + QStringLiteral(":/shaders/fragmentTexture")); } - initBackgroundShaders(QStringLiteral(":/shaders/vertexShadow"), - QStringLiteral(":/shaders/fragmentShadowNoTex")); - initCustomItemShaders(QStringLiteral(":/shaders/vertexShadow"), - QStringLiteral(":/shaders/fragmentShadow")); - } else { + initVolumeTextureShaders(QStringLiteral(":/shaders/vertexTexture3D"), + QStringLiteral(":/shaders/fragmentTexture3D"), + QStringLiteral(":/shaders/fragmentTexture3DLowDef"), + QStringLiteral(":/shaders/fragmentTexture3DSlice"), + QStringLiteral(":/shaders/vertexPosition"), + QStringLiteral(":/shaders/fragment3DSliceFrames")); + } else { if (m_cachedOptimizationHint.testFlag(QAbstract3DGraph::OptimizationStatic) && qobject_cast(this)) { initGradientShaders(QStringLiteral(":/shaders/vertexTexture"), - QStringLiteral(":/shaders/fragmentTexture")); + QStringLiteral(":/shaders/fragmentTextureES2")); initStaticSelectedItemShaders(QStringLiteral(":/shaders/vertex"), - QStringLiteral(":/shaders/fragment"), + QStringLiteral(":/shaders/fragmentES2"), QStringLiteral(":/shaders/vertex"), - QStringLiteral(":/shaders/fragmentColorOnY")); - initShaders(QStringLiteral(":/shaders/vertexNoMatrices"), - QStringLiteral(":/shaders/fragment")); + QStringLiteral(":/shaders/fragmentColorOnYES2")); + initBackgroundShaders(QStringLiteral(":/shaders/vertexNoMatrices"), + QStringLiteral(":/shaders/fragmentES2")); } else { initGradientShaders(QStringLiteral(":/shaders/vertex"), - QStringLiteral(":/shaders/fragmentColorOnY")); + QStringLiteral(":/shaders/fragmentColorOnYES2")); initShaders(QStringLiteral(":/shaders/vertex"), - QStringLiteral(":/shaders/fragment")); + QStringLiteral(":/shaders/fragmentES2")); } initBackgroundShaders(QStringLiteral(":/shaders/vertex"), - QStringLiteral(":/shaders/fragment")); - initCustomItemShaders(QStringLiteral(":/shaders/vertexTexture"), - QStringLiteral(":/shaders/fragmentTexture")); - } - initVolumeTextureShaders(QStringLiteral(":/shaders/vertexTexture3D"), - QStringLiteral(":/shaders/fragmentTexture3D"), - QStringLiteral(":/shaders/fragmentTexture3DLowDef"), - QStringLiteral(":/shaders/fragmentTexture3DSlice"), - QStringLiteral(":/shaders/vertexPosition"), - QStringLiteral(":/shaders/fragment3DSliceFrames")); -#else - if (m_cachedOptimizationHint.testFlag(QAbstract3DGraph::OptimizationStatic) - && qobject_cast(this)) { - initGradientShaders(QStringLiteral(":/shaders/vertexTexture"), - QStringLiteral(":/shaders/fragmentTextureES2")); - initStaticSelectedItemShaders(QStringLiteral(":/shaders/vertex"), - QStringLiteral(":/shaders/fragmentES2"), - QStringLiteral(":/shaders/vertex"), - QStringLiteral(":/shaders/fragmentColorOnYES2")); - initBackgroundShaders(QStringLiteral(":/shaders/vertexNoMatrices"), QStringLiteral(":/shaders/fragmentES2")); - } else { - initGradientShaders(QStringLiteral(":/shaders/vertex"), - QStringLiteral(":/shaders/fragmentColorOnYES2")); - initShaders(QStringLiteral(":/shaders/vertex"), - QStringLiteral(":/shaders/fragmentES2")); + initCustomItemShaders(QStringLiteral(":/shaders/vertexTexture"), + QStringLiteral(":/shaders/fragmentTextureES2")); } - initBackgroundShaders(QStringLiteral(":/shaders/vertex"), - QStringLiteral(":/shaders/fragmentES2")); - initCustomItemShaders(QStringLiteral(":/shaders/vertexTexture"), - QStringLiteral(":/shaders/fragmentTextureES2")); -#endif } void Abstract3DRenderer::handleShadowQualityChange() { reInitShaders(); -#if defined(QT_OPENGL_ES_2) - if (m_cachedShadowQuality != QAbstract3DGraph::ShadowQualityNone) { + if (m_isOpenGLES && m_cachedShadowQuality != QAbstract3DGraph::ShadowQualityNone) { emit requestShadowQuality(QAbstract3DGraph::ShadowQualityNone); qWarning("Shadows are not yet supported for OpenGL ES2"); m_cachedShadowQuality = QAbstract3DGraph::ShadowQualityNone; } -#endif } void Abstract3DRenderer::updateSelectionMode(QAbstract3DGraph::SelectionFlags mode) @@ -523,10 +545,8 @@ void Abstract3DRenderer::handleResize() // Re-init selection buffer initSelectionBuffer(); -#if !defined(QT_OPENGL_ES_2) // Re-init depth buffer updateDepthBuffer(); -#endif initCursorPositionBuffer(); } @@ -1111,8 +1131,7 @@ CustomRenderItem *Abstract3DRenderer::addCustomItem(QCustom3DItem *item) newItem->setOrigScaling(scaling); // Check if facing camera facingCamera = labelItem->isFacingCamera(); -#if !defined(QT_OPENGL_ES_2) - } else if (item->d_ptr->m_isVolumeItem) { + } else if (item->d_ptr->m_isVolumeItem && !m_isOpenGLES) { QCustom3DVolume *volumeItem = static_cast(item); newItem->setTextureWidth(volumeItem->textureWidth()); newItem->setTextureHeight(volumeItem->textureHeight()); @@ -1140,14 +1159,12 @@ CustomRenderItem *Abstract3DRenderer::addCustomItem(QCustom3DItem *item) newItem->setSliceFrameWidths(volumeItem->sliceFrameWidths()); newItem->setSliceFrameGaps(volumeItem->sliceFrameGaps()); newItem->setSliceFrameThicknesses(volumeItem->sliceFrameThicknesses()); -#endif } recalculateCustomItemScalingAndPos(newItem); newItem->setRotation(item->rotation()); -#if !defined(QT_OPENGL_ES_2) + // In OpenGL ES we simply draw volumes as regular custom item placeholders. - if (!item->d_ptr->m_isVolumeItem) -#endif + if (!item->d_ptr->m_isVolumeItem || m_isOpenGLES) { newItem->setBlendNeeded(textureImage.hasAlphaChannel()); texture = m_textureHelper->create2DTexture(textureImage, true, true, true); @@ -1291,17 +1308,13 @@ void Abstract3DRenderer::updateCustomItem(CustomRenderItem *renderItem) m_cachedTheme->isLabelBorderEnabled()); textureImage = item->d_ptr->textureImage(); } - } else -#if !defined(QT_OPENGL_ES_2) - if (!item->d_ptr->m_isVolumeItem) -#endif - { - renderItem->setBlendNeeded(textureImage.hasAlphaChannel()); - GLuint oldTexture = renderItem->texture(); - m_textureHelper->deleteTexture(&oldTexture); - GLuint texture = m_textureHelper->create2DTexture(textureImage, true, true, true); - renderItem->setTexture(texture); - } + } else if (!item->d_ptr->m_isVolumeItem || m_isOpenGLES) { + renderItem->setBlendNeeded(textureImage.hasAlphaChannel()); + GLuint oldTexture = renderItem->texture(); + m_textureHelper->deleteTexture(&oldTexture); + GLuint texture = m_textureHelper->create2DTexture(textureImage, true, true, true); + renderItem->setTexture(texture); + } item->d_ptr->clearTextureImage(); item->d_ptr->m_dirtyBits.textureDirty = false; } @@ -1319,8 +1332,7 @@ void Abstract3DRenderer::updateCustomItem(CustomRenderItem *renderItem) renderItem->setFacingCamera(labelItem->isFacingCamera()); labelItem->dptr()->m_facingCameraDirty = false; } -#if !defined(QT_OPENGL_ES_2) - } else if (item->d_ptr->m_isVolumeItem) { + } else if (item->d_ptr->m_isVolumeItem && !m_isOpenGLES) { QCustom3DVolume *volumeItem = static_cast(item); if (volumeItem->dptr()->m_dirtyBitsVolume.colorTableDirty) { renderItem->setColorTable(volumeItem->colorTable()); @@ -1366,7 +1378,6 @@ void Abstract3DRenderer::updateCustomItem(CustomRenderItem *renderItem) renderItem->setUseHighDefShader(volumeItem->useHighDefShader()); volumeItem->dptr()->m_dirtyBitsVolume.shaderDirty = false; } -#endif } } @@ -1481,8 +1492,7 @@ void Abstract3DRenderer::drawCustomItems(RenderingState state, if (RenderingNormal == state) { // Normal render ShaderHelper *prevShader = shader; -#if !defined(QT_OPENGL_ES_2) - if (item->isVolume()) { + if (item->isVolume() && !m_isOpenGLES) { if (item->drawSlices() && (item->sliceIndexX() >= 0 || item->sliceIndexY() >= 0 @@ -1493,12 +1503,11 @@ void Abstract3DRenderer::drawCustomItems(RenderingState state, } else { shader = m_volumeTextureLowDefShader; } - } else -#endif - if (item->isLabel()) + } else if (item->isLabel()) { shader = m_labelShader; - else + } else { shader = regularShader; + } if (shader != prevShader) shader->bind(); shader->setUniformValue(shader->model(), modelMatrix); @@ -1508,31 +1517,23 @@ void Abstract3DRenderer::drawCustomItems(RenderingState state, if (item->isBlendNeeded()) { glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); -#if !defined(QT_OPENGL_ES_2) - if (!item->isVolume()) -#endif + if (!item->isVolume() && !m_isOpenGLES) glDisable(GL_CULL_FACE); } else { glDisable(GL_BLEND); glEnable(GL_CULL_FACE); } -#if !defined(QT_OPENGL_ES_2) - if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone && !item->isVolume()) { + if (!m_isOpenGLES && m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone + && !item->isVolume()) { // Set shadow shader bindings shader->setUniformValue(shader->shadowQ(), shadowQuality); shader->setUniformValue(shader->depth(), depthProjectionViewMatrix * modelMatrix); shader->setUniformValue(shader->lightS(), m_cachedTheme->lightStrength() / 10.0f); m_drawer->drawObject(shader, item->mesh(), item->texture(), depthTexture); - } else -#else - Q_UNUSED(depthTexture) - Q_UNUSED(shadowQuality) -#endif - { + } else { // Set shadowless shader bindings -#if !defined(QT_OPENGL_ES_2) - if (item->isVolume()) { + if (item->isVolume() && !m_isOpenGLES) { QVector3D cameraPos = m_cachedScene->activeCamera()->position(); cameraPos = MVPMatrix.inverted().map(cameraPos); // Adjust camera position according to min/max bounds @@ -1598,9 +1599,7 @@ void Abstract3DRenderer::drawCustomItems(RenderingState state, shader->bind(); } m_drawer->drawObject(shader, item->mesh(), 0, 0, item->texture()); - } else -#endif - { + } else { shader->setUniformValue(shader->lightS(), m_cachedTheme->lightStrength()); m_drawer->drawObject(shader, item->mesh(), item->texture()); } @@ -1823,9 +1822,6 @@ void Abstract3DRenderer::drawRadialGrid(ShaderHelper *shader, float yFloorLinePo const QMatrix4x4 &projectionViewMatrix, const QMatrix4x4 &depthMatrix) { -#if defined(QT_OPENGL_ES_2) - Q_UNUSED(depthMatrix) -#endif static QVector lineRotations; if (!lineRotations.size()) { lineRotations.resize(polarGridRoundness); @@ -1865,20 +1861,20 @@ void Abstract3DRenderer::drawRadialGrid(ShaderHelper *shader, float yFloorLinePo shader->setUniformValue(shader->model(), modelMatrix); shader->setUniformValue(shader->nModel(), itModelMatrix.inverted().transposed()); shader->setUniformValue(shader->MVP(), MVPMatrix); -#if !defined(QT_OPENGL_ES_2) - if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { - // Set shadow shader bindings - QMatrix4x4 depthMVPMatrix = depthMatrix * modelMatrix; - shader->setUniformValue(shader->depth(), depthMVPMatrix); - // Draw the object - m_drawer->drawObject(shader, m_gridLineObj, 0, m_depthTexture); + if (!m_isOpenGLES) { + if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { + // Set shadow shader bindings + QMatrix4x4 depthMVPMatrix = depthMatrix * modelMatrix; + shader->setUniformValue(shader->depth(), depthMVPMatrix); + // Draw the object + m_drawer->drawObject(shader, m_gridLineObj, 0, m_depthTexture); + } else { + // Draw the object + m_drawer->drawObject(shader, m_gridLineObj); + } } else { - // Draw the object - m_drawer->drawObject(shader, m_gridLineObj); + m_drawer->drawLine(shader); } -#else - m_drawer->drawLine(shader); -#endif } } } @@ -1887,9 +1883,6 @@ void Abstract3DRenderer::drawAngularGrid(ShaderHelper *shader, float yFloorLineP const QMatrix4x4 &projectionViewMatrix, const QMatrix4x4 &depthMatrix) { -#if defined(QT_OPENGL_ES_2) - Q_UNUSED(depthMatrix) -#endif float halfRatio((m_polarRadius + (labelMargin / 2.0f)) / 2.0f); QVector3D gridLineScaler(gridLineWidth, gridLineWidth, halfRatio); int gridLineCount = m_axisCacheX.gridLineCount(); @@ -1897,11 +1890,11 @@ void Abstract3DRenderer::drawAngularGrid(ShaderHelper *shader, float yFloorLineP const QVector &subGridPositions = m_axisCacheX.formatter()->subGridPositions(); int mainSize = gridPositions.size(); QVector3D translateVector(0.0f, yFloorLinePos, -halfRatio); -#if defined(QT_OPENGL_ES_2) - QQuaternion finalRotation = m_yRightAngleRotationNeg; -#else - QQuaternion finalRotation = m_xRightAngleRotationNeg; -#endif + QQuaternion finalRotation; + if (m_isOpenGLES) + finalRotation = m_yRightAngleRotationNeg; + else + finalRotation = m_xRightAngleRotationNeg; if (m_yFlippedForGrid) finalRotation *= m_xFlipRotation; for (int i = 0; i < gridLineCount; i++) { @@ -1922,20 +1915,20 @@ void Abstract3DRenderer::drawAngularGrid(ShaderHelper *shader, float yFloorLineP shader->setUniformValue(shader->model(), modelMatrix); shader->setUniformValue(shader->nModel(), itModelMatrix.inverted().transposed()); shader->setUniformValue(shader->MVP(), MVPMatrix); -#if !defined(QT_OPENGL_ES_2) - if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { - // Set shadow shader bindings - QMatrix4x4 depthMVPMatrix = depthMatrix * modelMatrix; - shader->setUniformValue(shader->depth(), depthMVPMatrix); - // Draw the object - m_drawer->drawObject(shader, m_gridLineObj, 0, m_depthTexture); + if (m_isOpenGLES) { + m_drawer->drawLine(shader); } else { - // Draw the object - m_drawer->drawObject(shader, m_gridLineObj); + if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { + // Set shadow shader bindings + QMatrix4x4 depthMVPMatrix = depthMatrix * modelMatrix; + shader->setUniformValue(shader->depth(), depthMVPMatrix); + // Draw the object + m_drawer->drawObject(shader, m_gridLineObj, 0, m_depthTexture); + } else { + // Draw the object + m_drawer->drawObject(shader, m_gridLineObj); + } } -#else - m_drawer->drawLine(shader); -#endif } } diff --git a/src/datavisualization/engine/abstract3drenderer_p.h b/src/datavisualization/engine/abstract3drenderer_p.h index 38665c65..1e38023d 100644 --- a/src/datavisualization/engine/abstract3drenderer_p.h +++ b/src/datavisualization/engine/abstract3drenderer_p.h @@ -30,7 +30,9 @@ #define ABSTRACT3DRENDERER_P_H #include - +#if !defined(QT_OPENGL_ES_2) +# include +#endif #include "datavisualizationglobal_p.h" #include "abstract3dcontroller_p.h" #include "axisrendercache_p.h" @@ -83,9 +85,7 @@ public: virtual void initSelectionBuffer() = 0; virtual void updateSelectionState(SelectionState state); -#if !defined(QT_OPENGL_ES_2) virtual void updateDepthBuffer() = 0; -#endif virtual void updateShadowQuality(QAbstract3DGraph::ShadowQuality quality) = 0; virtual void initShaders(const QString &vertexShader, const QString &fragmentShader) = 0; virtual void initGradientShaders(const QString &vertexShader, const QString &fragmentShader); @@ -323,8 +323,12 @@ protected: qreal m_reflectivity; QLocale m_locale; +#if !defined(QT_OPENGL_ES_2) + QOpenGLFunctions_2_1 *m_funcs_2_1; // Not owned +#endif QPointer m_context; // Not owned QWindow *m_dummySurfaceAtDelete; + bool m_isOpenGLES; private: friend class Abstract3DController; diff --git a/src/datavisualization/engine/bars3drenderer.cpp b/src/datavisualization/engine/bars3drenderer.cpp index 07bb5f9a..b6a191a9 100644 --- a/src/datavisualization/engine/bars3drenderer.cpp +++ b/src/datavisualization/engine/bars3drenderer.cpp @@ -84,7 +84,6 @@ Bars3DRenderer::Bars3DRenderer(Bars3DController *controller) m_axisCacheY.setScale(2.0f); m_axisCacheY.setTranslate(-1.0f); - initializeOpenGLFunctions(); initializeOpenGL(); } @@ -113,10 +112,8 @@ void Bars3DRenderer::initializeOpenGL() // Initialize shaders -#if !defined(QT_OPENGL_ES_2) // Init depth shader (for shadows). Init in any case, easier to handle shadow activation if done via api. initDepthShader(); -#endif // Init selection shader initSelectionShader(); @@ -524,11 +521,12 @@ void Bars3DRenderer::drawSlicedScene() // Draw grid lines if (m_cachedTheme->isGridEnabled()) { glDisable(GL_DEPTH_TEST); -#if !(defined QT_OPENGL_ES_2) - ShaderHelper *lineShader = m_backgroundShader; -#else - ShaderHelper *lineShader = m_selectionShader; // Plain color shader for GL_LINES -#endif + ShaderHelper *lineShader; + if (m_isOpenGLES) + lineShader = m_selectionShader; // Plain color shader for GL_LINES + else + lineShader = m_backgroundShader; + // Bind line shader lineShader->bind(); @@ -567,11 +565,10 @@ void Bars3DRenderer::drawSlicedScene() lineShader->setUniformValue(lineShader->MVP(), MVPMatrix); // Draw the object -#if !(defined QT_OPENGL_ES_2) - m_drawer->drawObject(lineShader, m_gridLineObj); -#else - m_drawer->drawLine(lineShader); -#endif + if (m_isOpenGLES) + m_drawer->drawLine(lineShader); + else + m_drawer->drawObject(lineShader, m_gridLineObj); // Check if we have a line at zero position already if (gridPos == (barPosYAdjustment + zeroPosAdjustment)) @@ -596,11 +593,10 @@ void Bars3DRenderer::drawSlicedScene() m_cachedTheme->labelTextColor())); // Draw the object -#if !(defined QT_OPENGL_ES_2) - m_drawer->drawObject(lineShader, m_gridLineObj); -#else - m_drawer->drawLine(lineShader); -#endif + if (m_isOpenGLES) + m_drawer->drawLine(lineShader); + else + m_drawer->drawObject(lineShader, m_gridLineObj); } } @@ -1037,8 +1033,7 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) BarRenderItem *selectedBar(0); -#if !defined(QT_OPENGL_ES_2) - if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { + if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone && !m_isOpenGLES) { // Render scene into a depth texture for using with shadow mapping // Enable drawing to depth framebuffer glBindFramebuffer(GL_FRAMEBUFFER, m_depthFrameBuffer); @@ -1159,7 +1154,6 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) m_primarySubViewport.width(), m_primarySubViewport.height()); } -#endif // Do position mapping when necessary if (m_graphPositionQueryPending) { @@ -1643,10 +1637,10 @@ bool Bars3DRenderer::drawBars(BarRenderItem **selectedBar, qAbs(item.height()) / m_gradientFraction); } -#if !defined(QT_OPENGL_ES_2) - if ((m_reflectionEnabled && reflection == 1.0f + if (((m_reflectionEnabled && reflection == 1.0f && m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) - || m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { + || m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) + && !m_isOpenGLES) { // Set shadow shader bindings QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix; barShader->setUniformValue(barShader->shadowQ(), @@ -1658,12 +1652,7 @@ bool Bars3DRenderer::drawBars(BarRenderItem **selectedBar, // Draw the object m_drawer->drawObject(barShader, barObj, gradientTexture, m_depthTexture); - } else -#else - Q_UNUSED(shadowLightStrength); - Q_UNUSED(depthProjectionViewMatrix); -#endif - { + } else { // Set shadowless shader bindings if (m_reflectionEnabled && reflection != 1.0f && m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { @@ -1752,16 +1741,13 @@ void Bars3DRenderer::drawBackground(GLfloat backgroundRotation, shader->setUniformValue(shader->nModel(), itModelMatrix.inverted().transposed()); shader->setUniformValue(shader->MVP(), MVPMatrix); -#if !defined(QT_OPENGL_ES_2) - if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { + if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone && !m_isOpenGLES) { // Set shadow shader bindings QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix; shader->setUniformValue(shader->depth(), depthMVPMatrix); // Draw the object m_drawer->drawObject(shader, m_gridLineObj, 0, m_depthTexture); - } else -#endif - { + } else { // Draw the object m_drawer->drawObject(shader, m_gridLineObj); } @@ -1787,8 +1773,7 @@ void Bars3DRenderer::drawBackground(GLfloat backgroundRotation, shader->setUniformValue(shader->nModel(), itModelMatrix.inverted().transposed()); shader->setUniformValue(shader->MVP(), MVPMatrix); if (!m_reflectionEnabled || (m_reflectionEnabled && reflectingDraw)) { -#if !defined(QT_OPENGL_ES_2) - if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { + if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone && !m_isOpenGLES) { // Set shadow shader bindings QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix; shader->setUniformValue(shader->shadowQ(), m_shadowQualityToShader); @@ -1797,12 +1782,7 @@ void Bars3DRenderer::drawBackground(GLfloat backgroundRotation, // Draw the object m_drawer->drawObject(shader, m_backgroundObj, 0, m_depthTexture); - } else -#else - Q_UNUSED(adjustedLightStrength); - Q_UNUSED(depthProjectionViewMatrix); -#endif - { + } else { // Set shadowless shader bindings shader->setUniformValue(shader->lightS(), m_cachedTheme->lightStrength()); @@ -1818,12 +1798,12 @@ void Bars3DRenderer::drawGridLines(const QMatrix4x4 &depthProjectionViewMatrix, const QMatrix4x4 &viewMatrix) { if (m_cachedTheme->isGridEnabled()) { -#if !(defined QT_OPENGL_ES_2) - ShaderHelper *lineShader = m_backgroundShader; -#else - Q_UNUSED(depthProjectionViewMatrix); - ShaderHelper *lineShader = m_selectionShader; // Plain color shader for GL_LINES -#endif + ShaderHelper *lineShader; + if (m_isOpenGLES) + lineShader = m_selectionShader; // Plain color shader for GL_LINES + else + lineShader = m_backgroundShader; + QQuaternion lineRotation; QVector3D lightPos = m_cachedScene->activeLight()->position(); @@ -1839,15 +1819,12 @@ void Bars3DRenderer::drawGridLines(const QMatrix4x4 &depthProjectionViewMatrix, lineShader->setUniformValue(lineShader->color(), barColor); lineShader->setUniformValue(lineShader->ambientS(), m_cachedTheme->ambientLightStrength()); lineShader->setUniformValue(lineShader->lightColor(), lightColor); -#if !defined(QT_OPENGL_ES_2) - if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { + if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone && !m_isOpenGLES) { // Set shadowed shader bindings lineShader->setUniformValue(lineShader->shadowQ(), m_shadowQualityToShader); lineShader->setUniformValue(lineShader->lightS(), m_cachedTheme->lightStrength() / 20.0f); - } else -#endif - { + } else { // Set shadowless shader bindings lineShader->setUniformValue(lineShader->lightS(), m_cachedTheme->lightStrength() / 2.5f); @@ -1886,26 +1863,25 @@ void Bars3DRenderer::drawGridLines(const QMatrix4x4 &depthProjectionViewMatrix, itModelMatrix.inverted().transposed()); lineShader->setUniformValue(lineShader->MVP(), MVPMatrix); -#if !defined(QT_OPENGL_ES_2) - if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { - // Set shadow shader bindings - QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix; - lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix); - // Draw the object - m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture); + if (m_isOpenGLES) { + m_drawer->drawLine(lineShader); } else { - // Draw the object - m_drawer->drawObject(lineShader, m_gridLineObj); + if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { + // Set shadow shader bindings + QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix; + lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix); + // Draw the object + m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture); + } else { + // Draw the object + m_drawer->drawObject(lineShader, m_gridLineObj); + } } -#else - m_drawer->drawLine(lineShader); -#endif } // Floor lines: columns -#if defined(QT_OPENGL_ES_2) - lineRotation = m_yRightAngleRotation; -#endif + if (m_isOpenGLES) + lineRotation = m_yRightAngleRotation; gridLineScaler = QVector3D(gridLineWidth, gridLineWidth, m_scaleZWithBackground); for (GLfloat bar = 0.0f; bar <= m_cachedColumnCount; bar++) { QMatrix4x4 modelMatrix; @@ -1928,20 +1904,20 @@ void Bars3DRenderer::drawGridLines(const QMatrix4x4 &depthProjectionViewMatrix, itModelMatrix.inverted().transposed()); lineShader->setUniformValue(lineShader->MVP(), MVPMatrix); -#if !defined(QT_OPENGL_ES_2) - if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { - // Set shadow shader bindings - QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix; - lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix); - // Draw the object - m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture); + if (m_isOpenGLES) { + m_drawer->drawLine(lineShader); } else { - // Draw the object - m_drawer->drawObject(lineShader, m_gridLineObj); + if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { + // Set shadow shader bindings + QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix; + lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix); + // Draw the object + m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture); + } else { + // Draw the object + m_drawer->drawObject(lineShader, m_gridLineObj); + } } -#else - m_drawer->drawLine(lineShader); -#endif } if (m_axisCacheY.segmentCount() > 0) { @@ -1976,20 +1952,20 @@ void Bars3DRenderer::drawGridLines(const QMatrix4x4 &depthProjectionViewMatrix, itModelMatrix.inverted().transposed()); lineShader->setUniformValue(lineShader->MVP(), MVPMatrix); -#if !defined(QT_OPENGL_ES_2) - if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { - // Set shadow shader bindings - QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix; - lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix); - // Draw the object - m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture); + if (m_isOpenGLES) { + m_drawer->drawLine(lineShader); } else { - // Draw the object - m_drawer->drawObject(lineShader, m_gridLineObj); + if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { + // Set shadow shader bindings + QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix; + lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix); + // Draw the object + m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture); + } else { + // Draw the object + m_drawer->drawObject(lineShader, m_gridLineObj); + } } -#else - m_drawer->drawLine(lineShader); -#endif } // Wall lines: side wall @@ -2024,20 +2000,20 @@ void Bars3DRenderer::drawGridLines(const QMatrix4x4 &depthProjectionViewMatrix, itModelMatrix.inverted().transposed()); lineShader->setUniformValue(lineShader->MVP(), MVPMatrix); -#if !defined(QT_OPENGL_ES_2) - if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { - // Set shadow shader bindings - QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix; - lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix); - // Draw the object - m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture); + if (m_isOpenGLES) { + m_drawer->drawLine(lineShader); } else { - // Draw the object - m_drawer->drawObject(lineShader, m_gridLineObj); + if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { + // Set shadow shader bindings + QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix; + lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix); + // Draw the object + m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture); + } else { + // Draw the object + m_drawer->drawObject(lineShader, m_gridLineObj); + } } -#else - m_drawer->drawLine(lineShader); -#endif } } } @@ -2558,10 +2534,8 @@ void Bars3DRenderer::updateShadowQuality(QAbstract3DGraph::ShadowQuality quality handleShadowQualityChange(); -#if !defined(QT_OPENGL_ES_2) // Re-init depth buffer updateDepthBuffer(); -#endif // Redraw to handle both reflections and shadows on background if (m_reflectionEnabled) @@ -2767,10 +2741,7 @@ void Bars3DRenderer::updateSlicingActive(bool isSlicing) initCursorPositionBuffer(); } -#if !defined(QT_OPENGL_ES_2) updateDepthBuffer(); // Re-init depth buffer as well -#endif - m_selectionDirty = true; } @@ -2811,32 +2782,35 @@ void Bars3DRenderer::initSelectionBuffer() m_selectionDepthBuffer); } -#if !defined(QT_OPENGL_ES_2) void Bars3DRenderer::initDepthShader() { - if (m_depthShader) - delete m_depthShader; - m_depthShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexDepth"), - QStringLiteral(":/shaders/fragmentDepth")); - m_depthShader->initialize(); + if (!m_isOpenGLES) { + if (m_depthShader) + delete m_depthShader; + m_depthShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexDepth"), + QStringLiteral(":/shaders/fragmentDepth")); + m_depthShader->initialize(); + } } void Bars3DRenderer::updateDepthBuffer() { - m_textureHelper->deleteTexture(&m_depthTexture); + if (!m_isOpenGLES) { + m_textureHelper->deleteTexture(&m_depthTexture); - if (m_primarySubViewport.size().isEmpty()) - return; + if (m_primarySubViewport.size().isEmpty()) + return; - if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { - m_depthTexture = m_textureHelper->createDepthTextureFrameBuffer(m_primarySubViewport.size(), - m_depthFrameBuffer, - m_shadowQualityMultiplier); - if (!m_depthTexture) - lowerShadowQuality(); + if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { + m_depthTexture = + m_textureHelper->createDepthTextureFrameBuffer(m_primarySubViewport.size(), + m_depthFrameBuffer, + m_shadowQualityMultiplier); + if (!m_depthTexture) + lowerShadowQuality(); + } } } -#endif void Bars3DRenderer::initBackgroundShaders(const QString &vertexShader, const QString &fragmentShader) diff --git a/src/datavisualization/engine/bars3drenderer_p.h b/src/datavisualization/engine/bars3drenderer_p.h index 726bbffe..094b8fd9 100644 --- a/src/datavisualization/engine/bars3drenderer_p.h +++ b/src/datavisualization/engine/bars3drenderer_p.h @@ -168,10 +168,8 @@ private: void initSelectionShader(); void initBackgroundShaders(const QString &vertexShader, const QString &fragmentShader); void initSelectionBuffer(); -#if !defined(QT_OPENGL_ES_2) void initDepthShader(); void updateDepthBuffer(); -#endif void calculateSceneScalingFactors(); void calculateHeightAdjustment(); Abstract3DController::SelectionType isSelected(int row, int bar, diff --git a/src/datavisualization/engine/drawer.cpp b/src/datavisualization/engine/drawer.cpp index 939d4827..d4352702 100644 --- a/src/datavisualization/engine/drawer.cpp +++ b/src/datavisualization/engine/drawer.cpp @@ -69,10 +69,9 @@ Drawer::~Drawer() void Drawer::initializeOpenGL() { - if (!m_textureHelper) { - initializeOpenGLFunctions(); + initializeOpenGLFunctions(); + if (!m_textureHelper) m_textureHelper = new TextureHelper(); - } } void Drawer::setTheme(Q3DTheme *theme) diff --git a/src/datavisualization/engine/qabstract3dgraph.cpp b/src/datavisualization/engine/qabstract3dgraph.cpp index 96c82cb6..e51d9ce4 100644 --- a/src/datavisualization/engine/qabstract3dgraph.cpp +++ b/src/datavisualization/engine/qabstract3dgraph.cpp @@ -22,6 +22,7 @@ #include "qabstract3dinputhandler_p.h" #include "q3dscene_p.h" #include "qutils.h" +#include "utils_p.h" #include #include @@ -172,11 +173,7 @@ QAbstract3DGraph::QAbstract3DGraph(QAbstract3DGraphPrivate *d, const QSurfaceFor if (format) { surfaceFormat = *format; // Make sure renderable type is correct -#if !defined(QT_OPENGL_ES_2) - surfaceFormat.setRenderableType(QSurfaceFormat::OpenGL); -#else - surfaceFormat.setRenderableType(QSurfaceFormat::OpenGLES); -#endif + surfaceFormat.setRenderableType(QSurfaceFormat::DefaultRenderableType); } else { surfaceFormat = qDefaultSurfaceFormat(); } @@ -200,15 +197,13 @@ QAbstract3DGraph::QAbstract3DGraph(QAbstract3DGraphPrivate *d, const QSurfaceFor qDebug() << "GLSL version:" << (const char *)shaderVersion; #endif -#if !defined(QT_OPENGL_ES_2) - // If we have real OpenGL, GLSL version must be 1.2 or over. Quit if not. - QStringList splitversionstr = - QString::fromLatin1((const char *)shaderVersion).split(QChar::fromLatin1(' ')); - if (splitversionstr[0].toFloat() < 1.2) - qFatal("GLSL version must be 1.20 or higher. Try installing latest display drivers."); -#else - Q_UNUSED(shaderVersion) -#endif + if (!Utils::isOpenGLES()) { + // If we have real OpenGL, GLSL version must be 1.2 or over. Quit if not. + QStringList splitversionstr = + QString::fromLatin1((const char *)shaderVersion).split(QChar::fromLatin1(' ')); + if (splitversionstr[0].toFloat() < 1.2) + qFatal("GLSL version must be 1.20 or higher. Try installing latest display drivers."); + } d_ptr->renderLater(); @@ -1094,12 +1089,10 @@ QImage QAbstract3DGraphPrivate::renderToImage(int msaaSamples, const QSize &imag // Render the wanted frame offscreen m_context->makeCurrent(m_offscreenSurface); fboFormat.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil); -#ifdef QT_OPENGL_ES_2 - Q_UNUSED(msaaSamples); -#else - fboFormat.setInternalTextureFormat(GL_RGB); - fboFormat.setSamples(msaaSamples); -#endif + if (!Utils::isOpenGLES()) { + fboFormat.setInternalTextureFormat(GL_RGB); + fboFormat.setSamples(msaaSamples); + } fbo = new QOpenGLFramebufferObject(imageSize, fboFormat); if (fbo->isValid()) { QRect originalViewport = m_visualController->m_scene->viewport(); diff --git a/src/datavisualization/engine/scatter3drenderer.cpp b/src/datavisualization/engine/scatter3drenderer.cpp index d2f085be..f6367153 100644 --- a/src/datavisualization/engine/scatter3drenderer.cpp +++ b/src/datavisualization/engine/scatter3drenderer.cpp @@ -45,9 +45,7 @@ Scatter3DRenderer::Scatter3DRenderer(Scatter3DController *controller) m_dotGradientShader(0), m_staticSelectedItemGradientShader(0), m_staticSelectedItemShader(0), - #if defined(QT_OPENGL_ES_2) m_pointShader(0), - #endif m_depthShader(0), m_selectionShader(0), m_backgroundShader(0), @@ -73,7 +71,6 @@ Scatter3DRenderer::Scatter3DRenderer(Scatter3DController *controller) m_haveUniformColorMeshSeries(false), m_haveGradientMeshSeries(false) { - initializeOpenGLFunctions(); initializeOpenGL(); } @@ -104,22 +101,16 @@ void Scatter3DRenderer::initializeOpenGL() // Initialize shaders -#if !defined(QT_OPENGL_ES_2) - // Init depth shader (for shadows). Init in any case, easier to handle shadow activation if done via api. - initDepthShader(); -#else - // Init point shader - initPointShader(); -#endif + if (!m_isOpenGLES) { + initDepthShader(); // For shadows + loadGridLineMesh(); + } else { + initPointShader(); + } // Init selection shader initSelectionShader(); -#if !defined(QT_OPENGL_ES_2) - // Load grid line mesh - loadGridLineMesh(); -#endif - // Set view port glViewport(m_primarySubViewport.x(), m_primarySubViewport.y(), @@ -443,12 +434,11 @@ void Scatter3DRenderer::updateOptimizationHint(QAbstract3DGraph::OptimizationHin Abstract3DRenderer::reInitShaders(); -#if defined(QT_OPENGL_ES_2) - if (hint.testFlag(QAbstract3DGraph::OptimizationStatic) && !m_staticGradientPointShader) { + if (m_isOpenGLES && hint.testFlag(QAbstract3DGraph::OptimizationStatic) + && !m_staticGradientPointShader) { initStaticPointShaders(QStringLiteral(":/shaders/vertexPointES2_UV"), QStringLiteral(":/shaders/fragmentLabel")); } -#endif } void Scatter3DRenderer::updateMargin(float margin) @@ -546,164 +536,168 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) QVector3D lightPos = m_cachedScene->activeLight()->position(); // Introduce regardless of shadow quality to simplify logic - QMatrix4x4 depthViewMatrix; - QMatrix4x4 depthProjectionMatrix; QMatrix4x4 depthProjectionViewMatrix; + ShaderHelper *pointSelectionShader; + if (!m_isOpenGLES) { #if !defined(QT_OPENGL_ES_2) - if (m_havePointSeries) { - glEnable(GL_POINT_SMOOTH); - glEnable(GL_PROGRAM_POINT_SIZE); - } - - if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { - // Render scene into a depth texture for using with shadow mapping - // Bind depth shader - m_depthShader->bind(); - - // Set viewport for depth map rendering. Must match texture size. Larger values give smoother shadows. - glViewport(0, 0, - m_primarySubViewport.width() * m_shadowQualityMultiplier, - m_primarySubViewport.height() * m_shadowQualityMultiplier); - - // Enable drawing to framebuffer - glBindFramebuffer(GL_FRAMEBUFFER, m_depthFrameBuffer); - glClear(GL_DEPTH_BUFFER_BIT); - - // Set front face culling to reduce self-shadowing issues - glCullFace(GL_FRONT); - - // Get the depth view matrix - // It may be possible to hack lightPos here if we want to make some tweaks to shadow - QVector3D depthLightPos = activeCamera->d_ptr->calculatePositionRelativeToCamera( - zeroVector, 0.0f, 2.5f / m_autoScaleAdjustment); - depthViewMatrix.lookAt(depthLightPos, zeroVector, upVector); - // Set the depth projection matrix - depthProjectionMatrix.perspective(15.0f, viewPortRatio, 3.0f, 100.0f); - depthProjectionViewMatrix = depthProjectionMatrix * depthViewMatrix; - - // Draw dots to depth buffer - foreach (SeriesRenderCache *baseCache, m_renderCacheList) { - if (baseCache->isVisible()) { - ScatterSeriesRenderCache *cache = - static_cast(baseCache); - ObjectHelper *dotObj = cache->object(); - QQuaternion seriesRotation(cache->meshRotation()); - const ScatterRenderItemArray &renderArray = cache->renderArray(); - const int renderArraySize = renderArray.size(); - bool drawingPoints = (cache->mesh() == QAbstract3DSeries::MeshPoint); - float itemSize = cache->itemSize() / itemScaler; - if (itemSize == 0.0f) - itemSize = m_dotSizeScale; - if (drawingPoints) { - // Scale points based on shadow quality for shadows, not by zoom level - glPointSize(itemSize * 100.0f * m_shadowQualityMultiplier); - } - QVector3D modelScaler(itemSize, itemSize, itemSize); + if (m_havePointSeries) { + glEnable(GL_POINT_SMOOTH); + glEnable(GL_PROGRAM_POINT_SIZE); + } - if (!optimizationDefault - && ((drawingPoints && cache->bufferPoints()->indexCount() == 0) - || (!drawingPoints && cache->bufferObject()->indexCount() == 0))) { - continue; - } + if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { + // Render scene into a depth texture for using with shadow mapping + // Bind depth shader + m_depthShader->bind(); + + // Set viewport for depth map rendering. Must match texture size. Larger values give smoother shadows. + glViewport(0, 0, + m_primarySubViewport.width() * m_shadowQualityMultiplier, + m_primarySubViewport.height() * m_shadowQualityMultiplier); + + // Enable drawing to framebuffer + glBindFramebuffer(GL_FRAMEBUFFER, m_depthFrameBuffer); + glClear(GL_DEPTH_BUFFER_BIT); + + // Set front face culling to reduce self-shadowing issues + glCullFace(GL_FRONT); + + QMatrix4x4 depthViewMatrix; + QMatrix4x4 depthProjectionMatrix; + + // Get the depth view matrix + // It may be possible to hack lightPos here if we want to make some tweaks to shadow + QVector3D depthLightPos = activeCamera->d_ptr->calculatePositionRelativeToCamera( + zeroVector, 0.0f, 2.5f / m_autoScaleAdjustment); + depthViewMatrix.lookAt(depthLightPos, zeroVector, upVector); + // Set the depth projection matrix + depthProjectionMatrix.perspective(15.0f, viewPortRatio, 3.0f, 100.0f); + depthProjectionViewMatrix = depthProjectionMatrix * depthViewMatrix; + + // Draw dots to depth buffer + foreach (SeriesRenderCache *baseCache, m_renderCacheList) { + if (baseCache->isVisible()) { + ScatterSeriesRenderCache *cache = + static_cast(baseCache); + ObjectHelper *dotObj = cache->object(); + QQuaternion seriesRotation(cache->meshRotation()); + const ScatterRenderItemArray &renderArray = cache->renderArray(); + const int renderArraySize = renderArray.size(); + bool drawingPoints = (cache->mesh() == QAbstract3DSeries::MeshPoint); + float itemSize = cache->itemSize() / itemScaler; + if (itemSize == 0.0f) + itemSize = m_dotSizeScale; + if (drawingPoints) { + // Scale points based on shadow quality for shadows, not by zoom level + m_funcs_2_1->glPointSize(itemSize * 100.0f * m_shadowQualityMultiplier); + } + QVector3D modelScaler(itemSize, itemSize, itemSize); - int loopCount = 1; - if (optimizationDefault) - loopCount = renderArraySize; - for (int dot = 0; dot < loopCount; dot++) { - const ScatterRenderItem &item = renderArray.at(dot); - if (!item.isVisible() && optimizationDefault) + if (!optimizationDefault + && ((drawingPoints && cache->bufferPoints()->indexCount() == 0) + || (!drawingPoints && cache->bufferObject()->indexCount() == 0))) { continue; + } - QMatrix4x4 modelMatrix; - QMatrix4x4 MVPMatrix; + int loopCount = 1; + if (optimizationDefault) + loopCount = renderArraySize; + for (int dot = 0; dot < loopCount; dot++) { + const ScatterRenderItem &item = renderArray.at(dot); + if (!item.isVisible() && optimizationDefault) + continue; - if (optimizationDefault) { - modelMatrix.translate(item.translation()); - if (!drawingPoints) { - if (!seriesRotation.isIdentity() || !item.rotation().isIdentity()) - modelMatrix.rotate(seriesRotation * item.rotation()); - modelMatrix.scale(modelScaler); + QMatrix4x4 modelMatrix; + QMatrix4x4 MVPMatrix; + + if (optimizationDefault) { + modelMatrix.translate(item.translation()); + if (!drawingPoints) { + if (!seriesRotation.isIdentity() || !item.rotation().isIdentity()) + modelMatrix.rotate(seriesRotation * item.rotation()); + modelMatrix.scale(modelScaler); + } } - } - MVPMatrix = depthProjectionViewMatrix * modelMatrix; + MVPMatrix = depthProjectionViewMatrix * modelMatrix; - m_depthShader->setUniformValue(m_depthShader->MVP(), MVPMatrix); + m_depthShader->setUniformValue(m_depthShader->MVP(), MVPMatrix); - if (drawingPoints) { - if (optimizationDefault) - m_drawer->drawPoint(m_depthShader); - else - m_drawer->drawPoints(m_depthShader, cache->bufferPoints(), 0); - } else { - if (optimizationDefault) { - // 1st attribute buffer : vertices - glEnableVertexAttribArray(m_depthShader->posAtt()); - glBindBuffer(GL_ARRAY_BUFFER, dotObj->vertexBuf()); - glVertexAttribPointer(m_depthShader->posAtt(), 3, GL_FLOAT, GL_FALSE, 0, - (void *)0); + if (drawingPoints) { + if (optimizationDefault) + m_drawer->drawPoint(m_depthShader); + else + m_drawer->drawPoints(m_depthShader, cache->bufferPoints(), 0); + } else { + if (optimizationDefault) { + // 1st attribute buffer : vertices + glEnableVertexAttribArray(m_depthShader->posAtt()); + glBindBuffer(GL_ARRAY_BUFFER, dotObj->vertexBuf()); + glVertexAttribPointer(m_depthShader->posAtt(), 3, GL_FLOAT, GL_FALSE, 0, + (void *)0); - // Index buffer - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, dotObj->elementBuf()); + // Index buffer + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, dotObj->elementBuf()); - // Draw the triangles - glDrawElements(GL_TRIANGLES, dotObj->indexCount(), GL_UNSIGNED_SHORT, - (void *)0); + // Draw the triangles + glDrawElements(GL_TRIANGLES, dotObj->indexCount(), GL_UNSIGNED_SHORT, + (void *)0); - // Free buffers - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); - glBindBuffer(GL_ARRAY_BUFFER, 0); + // Free buffers + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + glBindBuffer(GL_ARRAY_BUFFER, 0); - glDisableVertexAttribArray(m_depthShader->posAtt()); - } else { - ScatterObjectBufferHelper *object = cache->bufferObject(); - // 1st attribute buffer : vertices - glEnableVertexAttribArray(m_depthShader->posAtt()); - glBindBuffer(GL_ARRAY_BUFFER, object->vertexBuf()); - glVertexAttribPointer(m_depthShader->posAtt(), 3, GL_FLOAT, GL_FALSE, 0, - (void *)0); + glDisableVertexAttribArray(m_depthShader->posAtt()); + } else { + ScatterObjectBufferHelper *object = cache->bufferObject(); + // 1st attribute buffer : vertices + glEnableVertexAttribArray(m_depthShader->posAtt()); + glBindBuffer(GL_ARRAY_BUFFER, object->vertexBuf()); + glVertexAttribPointer(m_depthShader->posAtt(), 3, GL_FLOAT, GL_FALSE, 0, + (void *)0); - // Index buffer - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, object->elementBuf()); + // Index buffer + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, object->elementBuf()); - // Draw the triangles - glDrawElements(GL_TRIANGLES, object->indexCount(), - object->indicesType(), (void *)0); + // Draw the triangles + glDrawElements(GL_TRIANGLES, object->indexCount(), + object->indicesType(), (void *)0); - // Free buffers - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); - glBindBuffer(GL_ARRAY_BUFFER, 0); + // Free buffers + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + glBindBuffer(GL_ARRAY_BUFFER, 0); - glDisableVertexAttribArray(m_depthShader->posAtt()); + glDisableVertexAttribArray(m_depthShader->posAtt()); + } } } } } - } - Abstract3DRenderer::drawCustomItems(RenderingDepth, m_depthShader, viewMatrix, - projectionViewMatrix, - depthProjectionViewMatrix, m_depthTexture, - m_shadowQualityToShader); + Abstract3DRenderer::drawCustomItems(RenderingDepth, m_depthShader, viewMatrix, + projectionViewMatrix, + depthProjectionViewMatrix, m_depthTexture, + m_shadowQualityToShader); - // Disable drawing to framebuffer (= enable drawing to screen) - glBindFramebuffer(GL_FRAMEBUFFER, defaultFboHandle); + // Disable drawing to framebuffer (= enable drawing to screen) + glBindFramebuffer(GL_FRAMEBUFFER, defaultFboHandle); - // Reset culling to normal - glCullFace(GL_BACK); + // Reset culling to normal + glCullFace(GL_BACK); - // Revert to original viewport - glViewport(m_primarySubViewport.x(), - m_primarySubViewport.y(), - m_primarySubViewport.width(), - m_primarySubViewport.height()); + // Revert to original viewport + glViewport(m_primarySubViewport.x(), + m_primarySubViewport.y(), + m_primarySubViewport.width(), + m_primarySubViewport.height()); + } +#endif + pointSelectionShader = m_selectionShader; + } else { + pointSelectionShader = m_pointShader; } - ShaderHelper *pointSelectionShader = m_selectionShader; -#else - ShaderHelper *pointSelectionShader = m_pointShader; -#endif ShaderHelper *selectionShader = m_selectionShader; // Do position mapping when necessary @@ -744,8 +738,8 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) if (itemSize == 0.0f) itemSize = m_dotSizeScale; #if !defined(QT_OPENGL_ES_2) - if (drawingPoints) - glPointSize(itemSize * activeCamera->zoomLevel()); // Scale points based on zoom + if (drawingPoints && !m_isOpenGLES) + m_funcs_2_1->glPointSize(itemSize * activeCamera->zoomLevel()); #endif QVector3D modelScaler(itemSize, itemSize, itemSize); @@ -877,8 +871,8 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) if (itemSize == 0.0f) itemSize = m_dotSizeScale; #if !defined(QT_OPENGL_ES_2) - if (drawingPoints) - glPointSize(itemSize * activeCamera->zoomLevel()); // Scale points based on zoom + if (drawingPoints && !m_isOpenGLES) + m_funcs_2_1->glPointSize(itemSize * activeCamera->zoomLevel()); #endif QVector3D modelScaler(itemSize, itemSize, itemSize); int gradientImageHeight = cache->gradientImage().height(); @@ -899,11 +893,10 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) previousDrawingPoints = drawingPoints; if (drawingPoints) { if (!optimizationDefault && rangeGradientPoints) { -#if !defined(QT_OPENGL_ES_2) - dotShader = m_labelShader; -#else - dotShader = m_staticGradientPointShader; -#endif + if (m_isOpenGLES) + dotShader = m_staticGradientPointShader; + else + dotShader = m_labelShader; } else { dotShader = pointSelectionShader; } @@ -938,6 +931,7 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) int loopCount = 1; if (optimizationDefault) loopCount = renderArraySize; + for (int i = 0; i < loopCount; i++) { ScatterRenderItem &item = renderArray[i]; if (!item.isVisible() && optimizationDefault) @@ -1013,8 +1007,7 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) (item.translation().y() + m_scaleY) * rangeGradientYScaler); } -#if !defined(QT_OPENGL_ES_2) - if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { + if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone && !m_isOpenGLES) { if (!drawingPoints) { // Set shadow shader bindings QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix; @@ -1037,9 +1030,7 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) else m_drawer->drawPoints(dotShader, cache->bufferPoints(), gradientTexture); } - } else -#endif - { + } else { if (!drawingPoints) { // Set shadowless shader bindings dotShader->setUniformValue(dotShader->lightS(), lightStrength); @@ -1058,6 +1049,7 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) } } + // Draw the selected item on static optimization if (!optimizationDefault && selectedSeries && m_selectedItemIndex != Scatter3DController::invalidSelectionIndex()) { @@ -1149,8 +1141,8 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) glPolygonOffset(-1.0f, 1.0f); } -#if !defined(QT_OPENGL_ES_2) - if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { + if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone + && !m_isOpenGLES) { if (!drawingPoints) { // Set shadow shader bindings QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix; @@ -1168,9 +1160,7 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) // Draw the object m_drawer->drawPoint(selectionShader); } - } else -#endif - { + } else { if (!drawingPoints) { // Set shadowless shader bindings selectionShader->setUniformValue(selectionShader->lightS(), @@ -1240,8 +1230,7 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) m_cachedTheme->ambientLightStrength() * 2.0f); m_backgroundShader->setUniformValue(m_backgroundShader->lightColor(), lightColor); -#if !defined(QT_OPENGL_ES_2) - if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { + if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone && !m_isOpenGLES) { // Set shadow shader bindings QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix; m_backgroundShader->setUniformValue(m_backgroundShader->shadowQ(), @@ -1252,9 +1241,7 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) // Draw the object m_drawer->drawObject(m_backgroundShader, m_backgroundObj, 0, m_depthTexture); - } else -#endif - { + } else { // Set shadowless shader bindings m_backgroundShader->setUniformValue(m_backgroundShader->lightS(), m_cachedTheme->lightStrength()); @@ -1270,11 +1257,11 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) QVector3D gridLineScaleY(gridLineWidth, m_scaleYWithBackground, gridLineWidth); if (m_cachedTheme->isGridEnabled()) { -#if !(defined QT_OPENGL_ES_2) - ShaderHelper *lineShader = m_backgroundShader; -#else - ShaderHelper *lineShader = m_selectionShader; // Plain color shader for GL_LINES -#endif + ShaderHelper *lineShader; + if (m_isOpenGLES) + lineShader = m_selectionShader; // Plain color shader for GL_LINES + else + lineShader = m_backgroundShader; // Bind line shader lineShader->bind(); @@ -1286,15 +1273,12 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) lineShader->setUniformValue(lineShader->color(), lineColor); lineShader->setUniformValue(lineShader->ambientS(), m_cachedTheme->ambientLightStrength()); lineShader->setUniformValue(lineShader->lightColor(), lightColor); -#if !defined(QT_OPENGL_ES_2) - if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { + if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone && !m_isOpenGLES) { // Set shadowed shader bindings lineShader->setUniformValue(lineShader->shadowQ(), m_shadowQualityToShader); lineShader->setUniformValue(lineShader->lightS(), m_cachedTheme->lightStrength() / 20.0f); - } else -#endif - { + } else { // Set shadowless shader bindings lineShader->setUniformValue(lineShader->lightS(), m_cachedTheme->lightStrength() / 2.5f); @@ -1347,20 +1331,20 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) itModelMatrix.inverted().transposed()); lineShader->setUniformValue(lineShader->MVP(), MVPMatrix); -#if !defined(QT_OPENGL_ES_2) - if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { - QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix; - // Set shadow shader bindings - lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix); - // Draw the object - m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture); + if (m_isOpenGLES) { + m_drawer->drawLine(lineShader); } else { - // Draw the object - m_drawer->drawObject(lineShader, m_gridLineObj); + if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { + QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix; + // Set shadow shader bindings + lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix); + // Draw the object + m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture); + } else { + // Draw the object + m_drawer->drawObject(lineShader, m_gridLineObj); + } } -#else - m_drawer->drawLine(lineShader); -#endif } // Side wall lines @@ -1379,13 +1363,13 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) modelMatrix.scale(gridLineScaleY); itModelMatrix.scale(gridLineScaleY); -#if !defined(QT_OPENGL_ES_2) - modelMatrix.rotate(lineYRotation); - itModelMatrix.rotate(lineYRotation); -#else - modelMatrix.rotate(m_zRightAngleRotation); - itModelMatrix.rotate(m_zRightAngleRotation); -#endif + if (m_isOpenGLES) { + modelMatrix.rotate(m_zRightAngleRotation); + itModelMatrix.rotate(m_zRightAngleRotation); + } else { + modelMatrix.rotate(lineYRotation); + itModelMatrix.rotate(lineYRotation); + } MVPMatrix = projectionViewMatrix * modelMatrix; @@ -1395,29 +1379,28 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) itModelMatrix.inverted().transposed()); lineShader->setUniformValue(lineShader->MVP(), MVPMatrix); -#if !defined(QT_OPENGL_ES_2) - if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { - // Set shadow shader bindings - QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix; - lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix); - // Draw the object - m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture); + if (!m_isOpenGLES) { + if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { + // Set shadow shader bindings + QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix; + lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix); + // Draw the object + m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture); + } else { + // Draw the object + m_drawer->drawObject(lineShader, m_gridLineObj); + } } else { - // Draw the object - m_drawer->drawObject(lineShader, m_gridLineObj); + m_drawer->drawLine(lineShader); } -#else - m_drawer->drawLine(lineShader); -#endif } } } // Columns (= X) if (m_axisCacheX.segmentCount() > 0) { -#if defined(QT_OPENGL_ES_2) - lineXRotation = m_yRightAngleRotation; -#endif + if (m_isOpenGLES) + lineXRotation = m_yRightAngleRotation; // Floor lines int gridLineCount = m_axisCacheX.gridLineCount(); @@ -1447,20 +1430,20 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) itModelMatrix.inverted().transposed()); lineShader->setUniformValue(lineShader->MVP(), MVPMatrix); -#if !defined(QT_OPENGL_ES_2) - if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { - // Set shadow shader bindings - QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix; - lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix); - // Draw the object - m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture); + if (!m_isOpenGLES) { + if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { + // Set shadow shader bindings + QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix; + lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix); + // Draw the object + m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture); + } else { + // Draw the object + m_drawer->drawObject(lineShader, m_gridLineObj); + } } else { - // Draw the object - m_drawer->drawObject(lineShader, m_gridLineObj); + m_drawer->drawLine(lineShader); } -#else - m_drawer->drawLine(lineShader); -#endif } // Back wall lines @@ -1479,15 +1462,15 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) modelMatrix.scale(gridLineScaleY); itModelMatrix.scale(gridLineScaleY); -#if !defined(QT_OPENGL_ES_2) - if (m_zFlipped) { - modelMatrix.rotate(m_xFlipRotation); - itModelMatrix.rotate(m_xFlipRotation); + if (m_isOpenGLES) { + modelMatrix.rotate(m_zRightAngleRotation); + itModelMatrix.rotate(m_zRightAngleRotation); + } else { + if (m_zFlipped) { + modelMatrix.rotate(m_xFlipRotation); + itModelMatrix.rotate(m_xFlipRotation); + } } -#else - modelMatrix.rotate(m_zRightAngleRotation); - itModelMatrix.rotate(m_zRightAngleRotation); -#endif MVPMatrix = projectionViewMatrix * modelMatrix; @@ -1497,20 +1480,20 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) itModelMatrix.inverted().transposed()); lineShader->setUniformValue(lineShader->MVP(), MVPMatrix); -#if !defined(QT_OPENGL_ES_2) - if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { - // Set shadow shader bindings - QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix; - lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix); - // Draw the object - m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture); + if (!m_isOpenGLES) { + if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { + // Set shadow shader bindings + QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix; + lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix); + // Draw the object + m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture); + } else { + // Draw the object + m_drawer->drawObject(lineShader, m_gridLineObj); + } } else { - // Draw the object - m_drawer->drawObject(lineShader, m_gridLineObj); + m_drawer->drawLine(lineShader); } -#else - m_drawer->drawLine(lineShader); -#endif } } } @@ -1548,20 +1531,20 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) itModelMatrix.inverted().transposed()); lineShader->setUniformValue(lineShader->MVP(), MVPMatrix); -#if !defined(QT_OPENGL_ES_2) - if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { - // Set shadow shader bindings - QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix; - lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix); - // Draw the object - m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture); + if (!m_isOpenGLES) { + if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { + // Set shadow shader bindings + QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix; + lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix); + // Draw the object + m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture); + } else { + // Draw the object + m_drawer->drawObject(lineShader, m_gridLineObj); + } } else { - // Draw the object - m_drawer->drawObject(lineShader, m_gridLineObj); + m_drawer->drawLine(lineShader); } -#else - m_drawer->drawLine(lineShader); -#endif } // Side wall @@ -1591,20 +1574,20 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) itModelMatrix.inverted().transposed()); lineShader->setUniformValue(lineShader->MVP(), MVPMatrix); -#if !defined(QT_OPENGL_ES_2) - if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { - // Set shadow shader bindings - QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix; - lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix); - // Draw the object - m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture); + if (!m_isOpenGLES) { + if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { + // Set shadow shader bindings + QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix; + lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix); + // Draw the object + m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture); + } else { + // Draw the object + m_drawer->drawObject(lineShader, m_gridLineObj); + } } else { - // Draw the object - m_drawer->drawObject(lineShader, m_gridLineObj); + m_drawer->drawLine(lineShader); } -#else - m_drawer->drawLine(lineShader); -#endif } } } @@ -2214,10 +2197,8 @@ void Scatter3DRenderer::updateShadowQuality(QAbstract3DGraph::ShadowQuality qual handleShadowQualityChange(); -#if !defined(QT_OPENGL_ES_2) // Re-init depth buffer updateDepthBuffer(); -#endif } void Scatter3DRenderer::loadBackgroundMesh() @@ -2377,41 +2358,45 @@ void Scatter3DRenderer::initSelectionBuffer() m_selectionDepthBuffer); } -#if !defined(QT_OPENGL_ES_2) void Scatter3DRenderer::initDepthShader() { - if (m_depthShader) - delete m_depthShader; - m_depthShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexDepth"), - QStringLiteral(":/shaders/fragmentDepth")); - m_depthShader->initialize(); + if (!m_isOpenGLES) { + if (m_depthShader) + delete m_depthShader; + m_depthShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexDepth"), + QStringLiteral(":/shaders/fragmentDepth")); + m_depthShader->initialize(); + } } void Scatter3DRenderer::updateDepthBuffer() { - m_textureHelper->deleteTexture(&m_depthTexture); + if (!m_isOpenGLES) { + m_textureHelper->deleteTexture(&m_depthTexture); - if (m_primarySubViewport.size().isEmpty()) - return; + if (m_primarySubViewport.size().isEmpty()) + return; - if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { - m_depthTexture = m_textureHelper->createDepthTextureFrameBuffer(m_primarySubViewport.size(), - m_depthFrameBuffer, - m_shadowQualityMultiplier); - if (!m_depthTexture) - lowerShadowQuality(); + if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { + m_depthTexture = m_textureHelper->createDepthTextureFrameBuffer(m_primarySubViewport.size(), + m_depthFrameBuffer, + m_shadowQualityMultiplier); + if (!m_depthTexture) + lowerShadowQuality(); + } } } -#else + void Scatter3DRenderer::initPointShader() { - if (m_pointShader) - delete m_pointShader; - m_pointShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexPointES2"), - QStringLiteral(":/shaders/fragmentPlainColor")); - m_pointShader->initialize(); + if (m_isOpenGLES) { + if (m_pointShader) + delete m_pointShader; + m_pointShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexPointES2"), + QStringLiteral(":/shaders/fragmentPlainColor")); + m_pointShader->initialize(); + } } -#endif void Scatter3DRenderer::initBackgroundShaders(const QString &vertexShader, const QString &fragmentShader) diff --git a/src/datavisualization/engine/scatter3drenderer_p.h b/src/datavisualization/engine/scatter3drenderer_p.h index f492fc05..b45b31a2 100644 --- a/src/datavisualization/engine/scatter3drenderer_p.h +++ b/src/datavisualization/engine/scatter3drenderer_p.h @@ -55,9 +55,7 @@ private: ShaderHelper *m_dotGradientShader; ShaderHelper *m_staticSelectedItemGradientShader; ShaderHelper *m_staticSelectedItemShader; -#if defined(QT_OPENGL_ES_2) ShaderHelper *m_pointShader; -#endif ShaderHelper *m_depthShader; ShaderHelper *m_selectionShader; ShaderHelper *m_backgroundShader; @@ -135,12 +133,9 @@ private: void initBackgroundShaders(const QString &vertexShader, const QString &fragmentShader); void initStaticPointShaders(const QString &vertexShader, const QString &fragmentShader); void initSelectionBuffer(); -#if !defined(QT_OPENGL_ES_2) void initDepthShader(); void updateDepthBuffer(); -#else void initPointShader(); -#endif void calculateTranslation(ScatterRenderItem &item); void calculateSceneScalingFactors(); diff --git a/src/datavisualization/engine/selectionpointer.cpp b/src/datavisualization/engine/selectionpointer.cpp index f7f8344b..b57ec511 100644 --- a/src/datavisualization/engine/selectionpointer.cpp +++ b/src/datavisualization/engine/selectionpointer.cpp @@ -252,15 +252,16 @@ void SelectionPointer::initShaders() // The shader for the small point ball if (m_pointShader) delete m_pointShader; -#if !defined(QT_OPENGL_ES_2) - m_pointShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertex"), - QStringLiteral(":/shaders/fragment")); -#else - m_pointShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertex"), - QStringLiteral(":/shaders/fragmentES2")); -#endif - m_pointShader->initialize(); + if (Utils::isOpenGLES()) { + m_pointShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertex"), + QStringLiteral(":/shaders/fragmentES2")); + } else { + m_pointShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertex"), + QStringLiteral(":/shaders/fragment")); + } + + m_pointShader->initialize(); } QT_END_NAMESPACE_DATAVISUALIZATION diff --git a/src/datavisualization/engine/seriesrendercache.cpp b/src/datavisualization/engine/seriesrendercache.cpp index 5fcc97f0..77af31c7 100644 --- a/src/datavisualization/engine/seriesrendercache.cpp +++ b/src/datavisualization/engine/seriesrendercache.cpp @@ -92,9 +92,8 @@ void SeriesRenderCache::populate(bool newSeries) meshFileName = QStringLiteral(":/defaultMeshes/arrow"); break; case QAbstract3DSeries::MeshPoint: -#if defined(QT_OPENGL_ES_2) - qWarning("QAbstract3DSeries::MeshPoint is not fully supported on OpenGL ES2"); -#endif + if (Utils::isOpenGLES()) + qWarning("QAbstract3DSeries::MeshPoint is not fully supported on OpenGL ES2"); break; default: // Default to cube diff --git a/src/datavisualization/engine/surface3drenderer.cpp b/src/datavisualization/engine/surface3drenderer.cpp index 743c6fe0..37d6b463 100644 --- a/src/datavisualization/engine/surface3drenderer.cpp +++ b/src/datavisualization/engine/surface3drenderer.cpp @@ -79,7 +79,6 @@ Surface3DRenderer::Surface3DRenderer(Surface3DController *controller) " Requires at least GLSL version 1.2 with GL_EXT_gpu_shader4 extension."; } - initializeOpenGLFunctions(); initializeOpenGL(); } @@ -115,19 +114,14 @@ void Surface3DRenderer::initializeOpenGL() // Initialize shaders initSurfaceShaders(); -#if !defined(QT_OPENGL_ES_2) - // Init depth shader (for shadows). Init in any case, easier to handle shadow activation if done via api. - initDepthShader(); -#endif + if (!m_isOpenGLES) { + initDepthShader(); // For shadows + loadGridLineMesh(); + } // Init selection shader initSelectionShaders(); -#if !(defined QT_OPENGL_ES_2) - // Load grid line mesh - loadGridLineMesh(); -#endif - // Resize in case we've missed resize events // Resize calls initSelectionBuffer and initDepthBuffer, so they don't need to be called here handleResize(); @@ -926,11 +920,11 @@ void Surface3DRenderer::drawSlicedScene() // Grid lines if (m_cachedTheme->isGridEnabled()) { -#if !(defined QT_OPENGL_ES_2) - ShaderHelper *lineShader = m_backgroundShader; -#else - ShaderHelper *lineShader = m_surfaceGridShader; // Plain color shader for GL_LINES -#endif + ShaderHelper *lineShader; + if (m_isOpenGLES) + lineShader = m_selectionShader; // Plain color shader for GL_LINES + else + lineShader = m_backgroundShader; // Bind line shader lineShader->bind(); @@ -970,11 +964,10 @@ void Surface3DRenderer::drawSlicedScene() lineShader->setUniformValue(lineShader->MVP(), MVPMatrix); // Draw the object -#if !(defined QT_OPENGL_ES_2) - m_drawer->drawObject(lineShader, m_gridLineObj); -#else - m_drawer->drawLine(lineShader); -#endif + if (m_isOpenGLES) + m_drawer->drawLine(lineShader); + else + m_drawer->drawObject(lineShader, m_gridLineObj); } } @@ -990,10 +983,11 @@ void Surface3DRenderer::drawSlicedScene() modelMatrix.translate(sliceCache.gridLinePosition(line), 0.0f, -1.0f); modelMatrix.scale(gridLineScaleY); itModelMatrix.scale(gridLineScaleY); -#if (defined QT_OPENGL_ES_2) - modelMatrix.rotate(m_zRightAngleRotation); - itModelMatrix.rotate(m_zRightAngleRotation); -#endif + + if (m_isOpenGLES) { + modelMatrix.rotate(m_zRightAngleRotation); + itModelMatrix.rotate(m_zRightAngleRotation); + } MVPMatrix = projectionViewMatrix * modelMatrix; @@ -1004,11 +998,10 @@ void Surface3DRenderer::drawSlicedScene() lineShader->setUniformValue(lineShader->MVP(), MVPMatrix); // Draw the object -#if !(defined QT_OPENGL_ES_2) - m_drawer->drawObject(lineShader, m_gridLineObj); -#else - m_drawer->drawLine(lineShader); -#endif + if (m_isOpenGLES) + m_drawer->drawLine(lineShader); + else + m_drawer->drawObject(lineShader, m_gridLineObj); } } @@ -1170,9 +1163,8 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) QMatrix4x4 depthProjectionViewMatrix; // Draw depth buffer -#if !defined(QT_OPENGL_ES_2) GLfloat adjustedLightStrength = m_cachedTheme->lightStrength() / 10.0f; - if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone && + if (!m_isOpenGLES && m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone && (!m_renderCacheList.isEmpty() || !m_customRenderCache.isEmpty())) { // Render scene into a depth texture for using with shadow mapping // Enable drawing to depth framebuffer @@ -1256,7 +1248,6 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) glEnable(GL_CULL_FACE); glCullFace(GL_BACK); } -#endif // Do position mapping when necessary if (m_graphPositionQueryPending) { @@ -1406,8 +1397,8 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) } } -#if !defined(QT_OPENGL_ES_2) - if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { + if (!m_isOpenGLES && + m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { // Set shadow shader bindings QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix; shader->setUniformValue(shader->shadowQ(), m_shadowQualityToShader); @@ -1417,13 +1408,9 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) // Draw the objects m_drawer->drawObject(shader, cache->surfaceObject(), texture, m_depthTexture); - } else -#endif - { + } else { // Set shadowless shader bindings - shader->setUniformValue(shader->lightS(), - m_cachedTheme->lightStrength()); - + shader->setUniformValue(shader->lightS(), m_cachedTheme->lightStrength()); // Draw the objects m_drawer->drawObject(shader, cache->surfaceObject(), texture); } @@ -1498,8 +1485,7 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) m_cachedTheme->ambientLightStrength() * 2.0f); m_backgroundShader->setUniformValue(m_backgroundShader->lightColor(), lightColor); -#if !defined(QT_OPENGL_ES_2) - if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { + if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone && !m_isOpenGLES) { // Set shadow shader bindings QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix; m_backgroundShader->setUniformValue(m_backgroundShader->shadowQ(), @@ -1512,11 +1498,7 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) m_drawer->drawObject(m_backgroundShader, m_backgroundObj, 0, m_noShadowTexture); else m_drawer->drawObject(m_backgroundShader, m_backgroundObj, 0, m_depthTexture); - } else -#else - Q_UNUSED(noShadows); -#endif - { + } else { // Set shadowless shader bindings m_backgroundShader->setUniformValue(m_backgroundShader->lightS(), m_cachedTheme->lightStrength()); @@ -1532,11 +1514,11 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) QVector3D gridLineScaleY(gridLineWidth, m_scaleYWithBackground, gridLineWidth); if (m_cachedTheme->isGridEnabled()) { -#if !(defined QT_OPENGL_ES_2) - ShaderHelper *lineShader = m_backgroundShader; -#else - ShaderHelper *lineShader = m_surfaceGridShader; // Plain color shader for GL_LINES -#endif + ShaderHelper *lineShader; + if (m_isOpenGLES) + lineShader = m_surfaceGridShader; // Plain color shader for GL_LINES + else + lineShader = m_backgroundShader; // Bind line shader lineShader->bind(); @@ -1548,15 +1530,12 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) lineShader->setUniformValue(lineShader->color(), lineColor); lineShader->setUniformValue(lineShader->ambientS(), m_cachedTheme->ambientLightStrength()); lineShader->setUniformValue(lineShader->lightColor(), lightColor); -#if !defined(QT_OPENGL_ES_2) - if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { + if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone && !m_isOpenGLES) { // Set shadowed shader bindings lineShader->setUniformValue(lineShader->shadowQ(), m_shadowQualityToShader); lineShader->setUniformValue(lineShader->lightS(), m_cachedTheme->lightStrength() / 20.0f); - } else -#endif - { + } else { // Set shadowless shader bindings lineShader->setUniformValue(lineShader->lightS(), m_cachedTheme->lightStrength() / 2.5f); @@ -1609,20 +1588,20 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) itModelMatrix.inverted().transposed()); lineShader->setUniformValue(lineShader->MVP(), MVPMatrix); -#if !defined(QT_OPENGL_ES_2) - if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { - // Set shadow shader bindings - QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix; - lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix); - // Draw the object - m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture); + if (!m_isOpenGLES) { + if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { + // Set shadow shader bindings + QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix; + lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix); + // Draw the object + m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture); + } else { + // Draw the object + m_drawer->drawObject(lineShader, m_gridLineObj); + } } else { - // Draw the object - m_drawer->drawObject(lineShader, m_gridLineObj); + m_drawer->drawLine(lineShader); } -#else - m_drawer->drawLine(lineShader); -#endif } // Side wall lines GLfloat lineXTrans = m_scaleXWithBackground - gridLineOffset; @@ -1640,13 +1619,13 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) modelMatrix.scale(gridLineScaleY); itModelMatrix.scale(gridLineScaleY); -#if !defined(QT_OPENGL_ES_2) - modelMatrix.rotate(lineYRotation); - itModelMatrix.rotate(lineYRotation); -#else - modelMatrix.rotate(m_zRightAngleRotation); - itModelMatrix.rotate(m_zRightAngleRotation); -#endif + if (m_isOpenGLES) { + modelMatrix.rotate(m_zRightAngleRotation); + itModelMatrix.rotate(m_zRightAngleRotation); + } else { + modelMatrix.rotate(lineYRotation); + itModelMatrix.rotate(lineYRotation); + } MVPMatrix = projectionViewMatrix * modelMatrix; @@ -1656,29 +1635,29 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) itModelMatrix.inverted().transposed()); lineShader->setUniformValue(lineShader->MVP(), MVPMatrix); -#if !defined(QT_OPENGL_ES_2) - if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { - // Set shadow shader bindings - QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix; - lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix); - // Draw the object - m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture); + if (!m_isOpenGLES) { + if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { + // Set shadow shader bindings + QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix; + lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix); + // Draw the object + m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture); + } else { + // Draw the object + m_drawer->drawObject(lineShader, m_gridLineObj); + } } else { - // Draw the object - m_drawer->drawObject(lineShader, m_gridLineObj); + m_drawer->drawLine(lineShader); } -#else - m_drawer->drawLine(lineShader); -#endif } } } // Columns (= X) if (m_axisCacheX.segmentCount() > 0) { -#if defined(QT_OPENGL_ES_2) - lineXRotation = m_yRightAngleRotation; -#endif + if (m_isOpenGLES) + lineXRotation = m_yRightAngleRotation; + // Floor lines int gridLineCount = m_axisCacheX.gridLineCount(); @@ -1708,20 +1687,20 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) itModelMatrix.inverted().transposed()); lineShader->setUniformValue(lineShader->MVP(), MVPMatrix); -#if !defined(QT_OPENGL_ES_2) - if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { - // Set shadow shader bindings - QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix; - lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix); - // Draw the object - m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture); + if (!m_isOpenGLES) { + if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { + // Set shadow shader bindings + QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix; + lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix); + // Draw the object + m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture); + } else { + // Draw the object + m_drawer->drawObject(lineShader, m_gridLineObj); + } } else { - // Draw the object - m_drawer->drawObject(lineShader, m_gridLineObj); + m_drawer->drawLine(lineShader); } -#else - m_drawer->drawLine(lineShader); -#endif } // Back wall lines @@ -1740,15 +1719,13 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) modelMatrix.scale(gridLineScaleY); itModelMatrix.scale(gridLineScaleY); -#if !defined(QT_OPENGL_ES_2) - if (m_zFlipped) { + if (m_isOpenGLES) { + modelMatrix.rotate(m_zRightAngleRotation); + itModelMatrix.rotate(m_zRightAngleRotation); + } else if (m_zFlipped) { modelMatrix.rotate(m_xFlipRotation); itModelMatrix.rotate(m_xFlipRotation); } -#else - modelMatrix.rotate(m_zRightAngleRotation); - itModelMatrix.rotate(m_zRightAngleRotation); -#endif MVPMatrix = projectionViewMatrix * modelMatrix; @@ -1758,20 +1735,20 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) itModelMatrix.inverted().transposed()); lineShader->setUniformValue(lineShader->MVP(), MVPMatrix); -#if !defined(QT_OPENGL_ES_2) - if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { - // Set shadow shader bindings - QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix; - lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix); - // Draw the object - m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture); + if (!m_isOpenGLES) { + if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { + // Set shadow shader bindings + QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix; + lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix); + // Draw the object + m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture); + } else { + // Draw the object + m_drawer->drawObject(lineShader, m_gridLineObj); + } } else { - // Draw the object - m_drawer->drawObject(lineShader, m_gridLineObj); + m_drawer->drawLine(lineShader); } -#else - m_drawer->drawLine(lineShader); -#endif } } } @@ -1809,20 +1786,20 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) itModelMatrix.inverted().transposed()); lineShader->setUniformValue(lineShader->MVP(), MVPMatrix); -#if !defined(QT_OPENGL_ES_2) - if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { - // Set shadow shader bindings - QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix; - lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix); - // Draw the object - m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture); + if (!m_isOpenGLES) { + if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { + // Set shadow shader bindings + QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix; + lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix); + // Draw the object + m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture); + } else { + // Draw the object + m_drawer->drawObject(lineShader, m_gridLineObj); + } } else { - // Draw the object - m_drawer->drawObject(lineShader, m_gridLineObj); + m_drawer->drawLine(lineShader); } -#else - m_drawer->drawLine(lineShader); -#endif } // Side wall @@ -1852,20 +1829,20 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) itModelMatrix.inverted().transposed()); lineShader->setUniformValue(lineShader->MVP(), MVPMatrix); -#if !defined(QT_OPENGL_ES_2) - if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { - // Set shadow shader bindings - QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix; - lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix); - // Draw the object - m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture); + if (!m_isOpenGLES) { + if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { + // Set shadow shader bindings + QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix; + lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix); + // Draw the object + m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture); + } else { + // Draw the object + m_drawer->drawObject(lineShader, m_gridLineObj); + } } else { - // Draw the object - m_drawer->drawObject(lineShader, m_gridLineObj); + m_drawer->drawLine(lineShader); } -#else - m_drawer->drawLine(lineShader); -#endif } } } @@ -2830,9 +2807,7 @@ void Surface3DRenderer::updateShadowQuality(QAbstract3DGraph::ShadowQuality qual handleShadowQualityChange(); -#if !defined(QT_OPENGL_ES_2) updateDepthBuffer(); -#endif } void Surface3DRenderer::updateTextures() @@ -2856,9 +2831,7 @@ void Surface3DRenderer::updateSlicingActive(bool isSlicing) initCursorPositionBuffer(); } -#if !defined(QT_OPENGL_ES_2) updateDepthBuffer(); // Re-init depth buffer as well -#endif m_selectionDirty = true; @@ -2881,53 +2854,54 @@ void Surface3DRenderer::initShaders(const QString &vertexShader, const QString & delete m_surfaceSliceFlatShader; delete m_surfaceSliceSmoothShader; -#if !defined(QT_OPENGL_ES_2) - if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { - m_surfaceSmoothShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexShadow"), - QStringLiteral(":/shaders/fragmentSurfaceShadowNoTex")); - m_surfaceTexturedSmoothShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexShadow"), - QStringLiteral(":/shaders/fragmentTexturedSurfaceShadow")); - } else { - m_surfaceSmoothShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertex"), - QStringLiteral(":/shaders/fragmentSurface")); - m_surfaceTexturedSmoothShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexTexture"), - QStringLiteral(":/shaders/fragmentTexture")); - } - m_surfaceSliceSmoothShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertex"), - QStringLiteral(":/shaders/fragmentSurface")); - if (m_flatSupported) { + if (!m_isOpenGLES) { if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { - m_surfaceFlatShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexSurfaceShadowFlat"), - QStringLiteral(":/shaders/fragmentSurfaceShadowFlat")); - m_surfaceTexturedFlatShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexSurfaceShadowFlat"), - QStringLiteral(":/shaders/fragmentTexturedSurfaceShadowFlat")); + m_surfaceSmoothShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexShadow"), + QStringLiteral(":/shaders/fragmentSurfaceShadowNoTex")); + m_surfaceTexturedSmoothShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexShadow"), + QStringLiteral(":/shaders/fragmentTexturedSurfaceShadow")); } else { - m_surfaceFlatShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexSurfaceFlat"), - QStringLiteral(":/shaders/fragmentSurfaceFlat")); - m_surfaceTexturedFlatShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexSurfaceFlat"), - QStringLiteral(":/shaders/fragmentSurfaceTexturedFlat")); + m_surfaceSmoothShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertex"), + QStringLiteral(":/shaders/fragmentSurface")); + m_surfaceTexturedSmoothShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexTexture"), + QStringLiteral(":/shaders/fragmentTexture")); + } + m_surfaceSliceSmoothShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertex"), + QStringLiteral(":/shaders/fragmentSurface")); + if (m_flatSupported) { + if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { + m_surfaceFlatShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexSurfaceShadowFlat"), + QStringLiteral(":/shaders/fragmentSurfaceShadowFlat")); + m_surfaceTexturedFlatShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexSurfaceShadowFlat"), + QStringLiteral(":/shaders/fragmentTexturedSurfaceShadowFlat")); + } else { + m_surfaceFlatShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexSurfaceFlat"), + QStringLiteral(":/shaders/fragmentSurfaceFlat")); + m_surfaceTexturedFlatShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexSurfaceFlat"), + QStringLiteral(":/shaders/fragmentSurfaceTexturedFlat")); + } + m_surfaceSliceFlatShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexSurfaceFlat"), + QStringLiteral(":/shaders/fragmentSurfaceFlat")); + } else { + m_surfaceFlatShader = 0; + m_surfaceSliceFlatShader = 0; + m_surfaceTexturedFlatShader = 0; } - m_surfaceSliceFlatShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexSurfaceFlat"), - QStringLiteral(":/shaders/fragmentSurfaceFlat")); } else { - m_surfaceFlatShader = 0; - m_surfaceSliceFlatShader = 0; - m_surfaceTexturedFlatShader = 0; + m_surfaceSmoothShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertex"), + QStringLiteral(":/shaders/fragmentSurfaceES2")); + m_surfaceFlatShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertex"), + QStringLiteral(":/shaders/fragmentSurfaceES2")); + m_surfaceTexturedSmoothShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexTexture"), + QStringLiteral(":/shaders/fragmentTextureES2")); + m_surfaceTexturedFlatShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexTexture"), + QStringLiteral(":/shaders/fragmentTextureES2")); + m_surfaceSliceSmoothShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertex"), + QStringLiteral(":/shaders/fragmentSurfaceES2")); + m_surfaceSliceFlatShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertex"), + QStringLiteral(":/shaders/fragmentSurfaceES2")); } -#else - m_surfaceSmoothShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertex"), - QStringLiteral(":/shaders/fragmentSurfaceES2")); - m_surfaceFlatShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertex"), - QStringLiteral(":/shaders/fragmentSurfaceES2")); - m_surfaceTexturedSmoothShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexTexture"), - QStringLiteral(":/shaders/fragmentTextureES2")); - m_surfaceTexturedFlatShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexTexture"), - QStringLiteral(":/shaders/fragmentTextureES2")); - m_surfaceSliceSmoothShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertex"), - QStringLiteral(":/shaders/fragmentSurfaceES2")); - m_surfaceSliceFlatShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertex"), - QStringLiteral(":/shaders/fragmentSurfaceES2")); -#endif + m_surfaceSmoothShader->initialize(); m_surfaceSliceSmoothShader->initialize(); m_surfaceTexturedSmoothShader->initialize(); @@ -2969,31 +2943,33 @@ void Surface3DRenderer::initSurfaceShaders() handleShadowQualityChange(); } -#if !defined(QT_OPENGL_ES_2) void Surface3DRenderer::initDepthShader() { - delete m_depthShader; - m_depthShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexDepth"), - QStringLiteral(":/shaders/fragmentDepth")); - m_depthShader->initialize(); + if (!m_isOpenGLES) { + delete m_depthShader; + m_depthShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexDepth"), + QStringLiteral(":/shaders/fragmentDepth")); + m_depthShader->initialize(); + } } void Surface3DRenderer::updateDepthBuffer() { - m_textureHelper->deleteTexture(&m_depthTexture); + if (!m_isOpenGLES) { + m_textureHelper->deleteTexture(&m_depthTexture); - if (m_primarySubViewport.size().isEmpty()) - return; + if (m_primarySubViewport.size().isEmpty()) + return; - if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { - m_depthTexture = m_textureHelper->createDepthTextureFrameBuffer(m_primarySubViewport.size(), - m_depthFrameBuffer, - m_shadowQualityMultiplier); - if (!m_depthTexture) - lowerShadowQuality(); + if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { + m_depthTexture = m_textureHelper->createDepthTextureFrameBuffer(m_primarySubViewport.size(), + m_depthFrameBuffer, + m_shadowQualityMultiplier); + if (!m_depthTexture) + lowerShadowQuality(); + } } } -#endif QVector3D Surface3DRenderer::convertPositionToTranslation(const QVector3D &position, bool isAbsolute) diff --git a/src/datavisualization/engine/surface3drenderer_p.h b/src/datavisualization/engine/surface3drenderer_p.h index 090fe8a9..57b6f9e6 100644 --- a/src/datavisualization/engine/surface3drenderer_p.h +++ b/src/datavisualization/engine/surface3drenderer_p.h @@ -144,9 +144,7 @@ private: void surfacePointSelected(const QPoint &point); void updateSelectionPoint(SurfaceSeriesRenderCache *cache, const QPoint &point, bool label); QPoint selectionIdToSurfacePoint(uint id); -#if !defined(QT_OPENGL_ES_2) void updateDepthBuffer(); -#endif void emitSelectedPointChanged(QPoint position); Q_DISABLE_COPY(Surface3DRenderer) diff --git a/src/datavisualization/utils/abstractobjecthelper.cpp b/src/datavisualization/utils/abstractobjecthelper.cpp index c350d096..40b3a45e 100644 --- a/src/datavisualization/utils/abstractobjecthelper.cpp +++ b/src/datavisualization/utils/abstractobjecthelper.cpp @@ -28,6 +28,7 @@ AbstractObjectHelper::AbstractObjectHelper() m_indexCount(0), m_meshDataLoaded(false) { + initializeOpenGLFunctions(); } AbstractObjectHelper::~AbstractObjectHelper() diff --git a/src/datavisualization/utils/objecthelper.cpp b/src/datavisualization/utils/objecthelper.cpp index b64e8c3f..4240d6f5 100644 --- a/src/datavisualization/utils/objecthelper.cpp +++ b/src/datavisualization/utils/objecthelper.cpp @@ -111,7 +111,6 @@ ObjectHelper *ObjectHelper::getObjectHelper(const Abstract3DRenderer *cacheId, void ObjectHelper::load() { - initializeOpenGLFunctions(); if (m_meshDataLoaded) { // Delete old data glDeleteBuffers(1, &m_vertexbuffer); diff --git a/src/datavisualization/utils/qutils.h b/src/datavisualization/utils/qutils.h index 1e0eb9f9..d4acfc99 100644 --- a/src/datavisualization/utils/qutils.h +++ b/src/datavisualization/utils/qutils.h @@ -30,12 +30,10 @@ inline static QSurfaceFormat qDefaultSurfaceFormat(bool antialias = true) surfaceFormat.setDepthBufferSize(24); surfaceFormat.setStencilBufferSize(8); surfaceFormat.setSwapBehavior(QSurfaceFormat::DoubleBuffer); -#if !defined(QT_OPENGL_ES_2) - surfaceFormat.setRenderableType(QSurfaceFormat::OpenGL); -#else + surfaceFormat.setRenderableType(QSurfaceFormat::DefaultRenderableType); +#if defined(QT_OPENGL_ES_2) // Antialias not supported for ES antialias = false; - surfaceFormat.setRenderableType(QSurfaceFormat::OpenGLES); surfaceFormat.setRedBufferSize(8); surfaceFormat.setBlueBufferSize(8); surfaceFormat.setGreenBufferSize(8); diff --git a/src/datavisualization/utils/scatterobjectbufferhelper.cpp b/src/datavisualization/utils/scatterobjectbufferhelper.cpp index 8925a7a4..44c84ae0 100644 --- a/src/datavisualization/utils/scatterobjectbufferhelper.cpp +++ b/src/datavisualization/utils/scatterobjectbufferhelper.cpp @@ -38,8 +38,6 @@ ScatterObjectBufferHelper::~ScatterObjectBufferHelper() void ScatterObjectBufferHelper::fullLoad(ScatterSeriesRenderCache *cache, qreal dotScale) { - initializeOpenGLFunctions(); - m_meshDataLoaded = false; m_indexCount = 0; diff --git a/src/datavisualization/utils/scatterpointbufferhelper.cpp b/src/datavisualization/utils/scatterpointbufferhelper.cpp index f15ce3ec..22e76f92 100644 --- a/src/datavisualization/utils/scatterpointbufferhelper.cpp +++ b/src/datavisualization/utils/scatterpointbufferhelper.cpp @@ -76,8 +76,6 @@ void ScatterPointBufferHelper::popPoint() void ScatterPointBufferHelper::load(ScatterSeriesRenderCache *cache) { - initializeOpenGLFunctions(); - ScatterRenderItemArray &renderArray = cache->renderArray(); const int renderArraySize = renderArray.size(); m_indexCount = 0; diff --git a/src/datavisualization/utils/surfaceobject.cpp b/src/datavisualization/utils/surfaceobject.cpp index 5498c8b3..b93030b1 100644 --- a/src/datavisualization/utils/surfaceobject.cpp +++ b/src/datavisualization/utils/surfaceobject.cpp @@ -37,7 +37,6 @@ SurfaceObject::SurfaceObject(Surface3DRenderer *renderer) m_oldDataDimension(-1) { m_indicesType = GL_UNSIGNED_INT; - initializeOpenGLFunctions(); glGenBuffers(1, &m_vertexbuffer); glGenBuffers(1, &m_normalbuffer); glGenBuffers(1, &m_uvbuffer); diff --git a/src/datavisualization/utils/texturehelper.cpp b/src/datavisualization/utils/texturehelper.cpp index 939ce901..3944fb0c 100644 --- a/src/datavisualization/utils/texturehelper.cpp +++ b/src/datavisualization/utils/texturehelper.cpp @@ -30,24 +30,24 @@ extern void discardDebugMsgs(QtMsgType type, const QMessageLogContext &context, TextureHelper::TextureHelper() { + initializeOpenGLFunctions(); #if !defined(QT_OPENGL_ES_2) - // Discard warnings about deprecated functions - QtMessageHandler handler = qInstallMessageHandler(discardDebugMsgs); + if (!Utils::isOpenGLES()) { + // Discard warnings about deprecated functions + QtMessageHandler handler = qInstallMessageHandler(discardDebugMsgs); - m_openGlFunctions_2_1 = new QOpenGLFunctions_2_1; - m_openGlFunctions_2_1->initializeOpenGLFunctions(); + m_openGlFunctions_2_1 = + QOpenGLContext::currentContext()->versionFunctions(); + m_openGlFunctions_2_1->initializeOpenGLFunctions(); - // Restore original message handler - qInstallMessageHandler(handler); + // Restore original message handler + qInstallMessageHandler(handler); + } #endif - initializeOpenGLFunctions(); } TextureHelper::~TextureHelper() { -#if !defined(QT_OPENGL_ES_2) - delete m_openGlFunctions_2_1; -#endif } GLuint TextureHelper::create2DTexture(const QImage &image, bool useTrilinearFiltering, @@ -58,16 +58,16 @@ GLuint TextureHelper::create2DTexture(const QImage &image, bool useTrilinearFilt QImage texImage = image; -#if defined(QT_OPENGL_ES_2) - GLuint imageWidth = Utils::getNearestPowerOfTwo(image.width()); - GLuint imageHeight = Utils::getNearestPowerOfTwo(image.height()); - if (smoothScale) { - texImage = image.scaled(imageWidth, imageHeight, Qt::IgnoreAspectRatio, - Qt::SmoothTransformation); - } else { - texImage = image.scaled(imageWidth, imageHeight, Qt::IgnoreAspectRatio); + if (!Utils::isOpenGLES()) { + GLuint imageWidth = Utils::getNearestPowerOfTwo(image.width()); + GLuint imageHeight = Utils::getNearestPowerOfTwo(image.height()); + if (smoothScale) { + texImage = image.scaled(imageWidth, imageHeight, Qt::IgnoreAspectRatio, + Qt::SmoothTransformation); + } else { + texImage = image.scaled(imageWidth, imageHeight, Qt::IgnoreAspectRatio); + } } -#endif GLuint textureId; glGenTextures(1, &textureId); @@ -93,16 +93,19 @@ GLuint TextureHelper::create2DTexture(const QImage &image, bool useTrilinearFilt return textureId; } -#if !defined(QT_OPENGL_ES_2) GLuint TextureHelper::create3DTexture(const QVector *data, int width, int height, int depth, QImage::Format dataFormat) { - if (!width || !height || !depth) + if (Utils::isOpenGLES() || !width || !height || !depth) return 0; + GLuint textureId = 0; +#if defined(QT_OPENGL_ES_2) + Q_UNUSED(dataFormat) + Q_UNUSED(data) +#else glEnable(GL_TEXTURE_3D); - GLuint textureId; glGenTextures(1, &textureId); glBindTexture(GL_TEXTURE_3D, textureId); glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); @@ -126,17 +129,15 @@ GLuint TextureHelper::create3DTexture(const QVector *data, int width, int } m_openGlFunctions_2_1->glTexImage3D(GL_TEXTURE_3D, 0, internalFormat, width, height, depth, 0, format, GL_UNSIGNED_BYTE, data->constData()); - status = glGetError(); if (status) qWarning() << __FUNCTION__ << "3D texture creation failed:" << status; glBindTexture(GL_TEXTURE_3D, 0); glDisable(GL_TEXTURE_3D); - +#endif return textureId; } -#endif GLuint TextureHelper::createCubeMapTexture(const QImage &image, bool useTrilinearFiltering) { @@ -184,11 +185,11 @@ GLuint TextureHelper::createSelectionTexture(const QSize &size, GLuint &frameBuf // glGetError docs advise to call glGetError in loop to clear all error flags while (status) status = glGetError(); -#if !defined(QT_OPENGL_ES_2) - glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, size.width(), size.height()); -#else - glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, size.width(), size.height()); -#endif + if (Utils::isOpenGLES()) + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, size.width(), size.height()); + else + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, size.width(), size.height()); + status = glGetError(); if (status) { qCritical() << "Selection texture render buffer creation failed:" << status; @@ -271,59 +272,64 @@ GLuint TextureHelper::createGradientTexture(const QLinearGradient &gradient) return create2DTexture(image, false, true, false, true); } -#if !defined(QT_OPENGL_ES_2) GLuint TextureHelper::createDepthTexture(const QSize &size, GLuint textureSize) { - GLuint depthtextureid; - - // Create depth texture for the shadow mapping - glGenTextures(1, &depthtextureid); - glBindTexture(GL_TEXTURE_2D, depthtextureid); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_ARB, GL_COMPARE_R_TO_TEXTURE_ARB); - glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, size.width() * textureSize, - size.height() * textureSize, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL); - glBindTexture(GL_TEXTURE_2D, 0); - + GLuint depthtextureid = 0; +#if defined(QT_OPENGL_ES_2) + Q_UNUSED(size) + Q_UNUSED(textureSize) +#else + if (!Utils::isOpenGLES()) { + // Create depth texture for the shadow mapping + glGenTextures(1, &depthtextureid); + glBindTexture(GL_TEXTURE_2D, depthtextureid); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_ARB, GL_COMPARE_R_TO_TEXTURE_ARB); + glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, size.width() * textureSize, + size.height() * textureSize, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL); + glBindTexture(GL_TEXTURE_2D, 0); + } +#endif return depthtextureid; } -#endif -#if !defined(QT_OPENGL_ES_2) GLuint TextureHelper::createDepthTextureFrameBuffer(const QSize &size, GLuint &frameBuffer, GLuint textureSize) { GLuint depthtextureid = createDepthTexture(size, textureSize); +#if defined(QT_OPENGL_ES_2) + Q_UNUSED(frameBuffer) +#else + if (!Utils::isOpenGLES()) { + // Create frame buffer + if (!frameBuffer) + glGenFramebuffers(1, &frameBuffer); + glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer); + + // Attach texture to depth attachment + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthtextureid, 0); + + m_openGlFunctions_2_1->glDrawBuffer(GL_NONE); + m_openGlFunctions_2_1->glReadBuffer(GL_NONE); + + // Verify that the frame buffer is complete + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + if (status != GL_FRAMEBUFFER_COMPLETE) { + qCritical() << "Depth texture frame buffer creation failed" << status; + glDeleteTextures(1, &depthtextureid); + depthtextureid = 0; + } - // Create frame buffer - if (!frameBuffer) - glGenFramebuffers(1, &frameBuffer); - glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer); - - // Attach texture to depth attachment - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthtextureid, 0); - - glDrawBuffer(GL_NONE); - glReadBuffer(GL_NONE); - - // Verify that the frame buffer is complete - GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); - if (status != GL_FRAMEBUFFER_COMPLETE) { - qCritical() << "Depth texture frame buffer creation failed" << status; - glDeleteTextures(1, &depthtextureid); - depthtextureid = 0; + // Restore the default framebuffer + glBindFramebuffer(GL_FRAMEBUFFER, 0); } - - // Restore the default framebuffer - glBindFramebuffer(GL_FRAMEBUFFER, 0); - +#endif return depthtextureid; } -#endif void TextureHelper::deleteTexture(GLuint *texture) { diff --git a/src/datavisualization/utils/texturehelper_p.h b/src/datavisualization/utils/texturehelper_p.h index 03080b2a..6c0aa3de 100644 --- a/src/datavisualization/utils/texturehelper_p.h +++ b/src/datavisualization/utils/texturehelper_p.h @@ -48,21 +48,17 @@ class TextureHelper : protected QOpenGLFunctions // Ownership of created texture is transferred to caller GLuint create2DTexture(const QImage &image, bool useTrilinearFiltering = false, bool convert = true, bool smoothScale = true, bool clampY = false); -#if !defined(QT_OPENGL_ES_2) GLuint create3DTexture(const QVector *data, int width, int height, int depth, QImage::Format dataFormat); -#endif GLuint createCubeMapTexture(const QImage &image, bool useTrilinearFiltering = false); // Returns selection texture and inserts generated framebuffers to framebuffer parameters GLuint createSelectionTexture(const QSize &size, GLuint &frameBuffer, GLuint &depthBuffer); GLuint createCursorPositionTexture(const QSize &size, GLuint &frameBuffer); GLuint createUniformTexture(const QColor &color); GLuint createGradientTexture(const QLinearGradient &gradient); -#if !defined(QT_OPENGL_ES_2) GLuint createDepthTexture(const QSize &size, GLuint textureSize); // Returns depth texture and inserts generated framebuffer to parameter GLuint createDepthTextureFrameBuffer(const QSize &size, GLuint &frameBuffer, GLuint textureSize); -#endif void deleteTexture(GLuint *texture); private: @@ -71,7 +67,7 @@ class TextureHelper : protected QOpenGLFunctions QRgb qt_gl_convertToGLFormatHelper(QRgb src_pixel, GLenum texture_format); #if !defined(QT_OPENGL_ES_2) - QOpenGLFunctions_2_1 *m_openGlFunctions_2_1; + QOpenGLFunctions_2_1 *m_openGlFunctions_2_1; // Not owned #endif friend class Bars3DRenderer; friend class Surface3DRenderer; diff --git a/src/datavisualization/utils/utils.cpp b/src/datavisualization/utils/utils.cpp index 534c79f9..cb64eb8f 100644 --- a/src/datavisualization/utils/utils.cpp +++ b/src/datavisualization/utils/utils.cpp @@ -17,6 +17,7 @@ ****************************************************************************/ #include "utils_p.h" +#include "qutils.h" #include @@ -55,65 +56,58 @@ QImage Utils::printTextToImage(const QFont &font, const QString &text, const QCo const QColor &txtColor, bool labelBackground, bool borders, int maxLabelWidth) { - if (maxTextureSize == 0) - glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize); - + if (maxTextureSize == 0) { + QOpenGLContext::currentContext()->functions()->glGetIntegerv( + GL_MAX_TEXTURE_SIZE, &maxTextureSize); + } GLuint paddingWidth = 20; -#if !defined(QT_OPENGL_ES_2) // TODO fix ifdef for dynamic OpenGL GLuint paddingHeight = 20; -#endif + GLuint prePadding = 20; + GLint targetWidth = maxTextureSize; + // Calculate text dimensions QFont valueFont = font; valueFont.setPointSize(textureFontSize); QFontMetrics valueFM(valueFont); int valueStrWidth = valueFM.width(text); -#if defined(QT_OPENGL_ES_2) + // ES2 needs to use maxLabelWidth always (when given) because of the power of 2 -issue. - if (maxLabelWidth) -#else - if (maxLabelWidth && labelBackground) -#endif + if (maxLabelWidth && (labelBackground || Utils::isOpenGLES())) valueStrWidth = maxLabelWidth; int valueStrHeight = valueFM.height(); valueStrWidth += paddingWidth / 2; // Fix clipping problem with skewed fonts (italic or italic-style) QSize labelSize; qreal fontRatio = 1.0; -#if defined(QT_OPENGL_ES_2) // TODO fix ifdef for dynamic OpenGL - // Test if text with slighly smaller font would fit into one step smaller texture - // ie. if the text is just exceeded the smaller texture boundary, it would - // make a label with large empty space - GLuint prePadding = 20; - GLint targetWidth = maxTextureSize; - uint testWidth = getNearestPowerOfTwo(valueStrWidth + prePadding) >> 1; - int diffToFit = (valueStrWidth + prePadding) - testWidth; - int maxSqueeze = int((valueStrWidth + prePadding) * 0.1f); - if (diffToFit < maxSqueeze && maxTextureSize > GLint(testWidth)) - targetWidth = testWidth; -#endif + if (Utils::isOpenGLES()) { + // Test if text with slighly smaller font would fit into one step smaller texture + // ie. if the text is just exceeded the smaller texture boundary, it would + // make a label with large empty space + uint testWidth = getNearestPowerOfTwo(valueStrWidth + prePadding) >> 1; + int diffToFit = (valueStrWidth + prePadding) - testWidth; + int maxSqueeze = int((valueStrWidth + prePadding) * 0.1f); + if (diffToFit < maxSqueeze && maxTextureSize > GLint(testWidth)) + targetWidth = testWidth; + } bool sizeOk = false; int currentFontSize = textureFontSize; do { -#if defined(QT_OPENGL_ES_2) // TODO fix ifdef for dynamic OpenGL - // ES2 can't handle textures with dimensions not in power of 2. Resize labels accordingly. - // Add some padding before converting to power of two to avoid too tight fit - labelSize = QSize(valueStrWidth + prePadding, valueStrHeight + prePadding); - labelSize.setWidth(getNearestPowerOfTwo(labelSize.width())); - labelSize.setHeight(getNearestPowerOfTwo(labelSize.height())); -#else - if (!labelBackground) - labelSize = QSize(valueStrWidth, valueStrHeight); - else - labelSize = QSize(valueStrWidth + paddingWidth * 2, valueStrHeight + paddingHeight * 2); -#endif + if (Utils::isOpenGLES()) { + // ES2 can't handle textures with dimensions not in power of 2. Resize labels accordingly. + // Add some padding before converting to power of two to avoid too tight fit + labelSize = QSize(valueStrWidth + prePadding, valueStrHeight + prePadding); + labelSize.setWidth(getNearestPowerOfTwo(labelSize.width())); + labelSize.setHeight(getNearestPowerOfTwo(labelSize.height())); + } else { + if (!labelBackground) + labelSize = QSize(valueStrWidth, valueStrHeight); + else + labelSize = QSize(valueStrWidth + paddingWidth * 2, valueStrHeight + paddingHeight * 2); + } -#if defined(QT_OPENGL_ES_2) if (!maxTextureSize || (labelSize.width() <= maxTextureSize - && labelSize.width() <= targetWidth)) { -#else - if (!maxTextureSize || labelSize.width() <= maxTextureSize) { -#endif + && (labelSize.width() <= targetWidth || !Utils::isOpenGLES()))) { // Make sure the label is not too wide sizeOk = true; } else if (--currentFontSize == 4) { @@ -124,11 +118,7 @@ QImage Utils::printTextToImage(const QFont &font, const QString &text, const QCo // Reduce font size and try again valueFont.setPointSize(currentFontSize); QFontMetrics currentValueFM(valueFont); -#if defined(QT_OPENGL_ES_2) - if (maxLabelWidth) -#else - if (maxLabelWidth && labelBackground) -#endif + if (maxLabelWidth && (labelBackground || Utils::isOpenGLES())) valueStrWidth = maxLabelWidth * fontRatio; else valueStrWidth = currentValueFM.width(text); @@ -149,18 +139,18 @@ QImage Utils::printTextToImage(const QFont &font, const QString &text, const QCo painter.setFont(valueFont); if (!labelBackground) { painter.setPen(txtColor); -#if defined(QT_OPENGL_ES_2) - painter.drawText((labelSize.width() - valueStrWidth) / 2.0f, - (labelSize.height() - valueStrHeight) / 2.0f, - valueStrWidth, valueStrHeight, - Qt::AlignCenter | Qt::AlignVCenter, - text); -#else - painter.drawText(0, 0, - valueStrWidth, valueStrHeight, - Qt::AlignCenter | Qt::AlignVCenter, - text); -#endif + if (Utils::isOpenGLES()) { + painter.drawText((labelSize.width() - valueStrWidth) / 2.0f, + (labelSize.height() - valueStrHeight) / 2.0f, + valueStrWidth, valueStrHeight, + Qt::AlignCenter | Qt::AlignVCenter, + text); + } else { + painter.drawText(0, 0, + valueStrWidth, valueStrHeight, + Qt::AlignCenter | Qt::AlignVCenter, + text); + } } else { painter.setBrush(QBrush(bgrColor)); qreal radius = 10.0 * fontRatio; @@ -189,8 +179,9 @@ QVector4D Utils::getSelection(QPoint mousepos, int height) // This is the only one that works with OpenGL ES 2.0, so we're forced to use it // Item count will be limited to 256*256*256 GLubyte pixel[4] = {255, 255, 255, 255}; - glReadPixels(mousepos.x(), height - mousepos.y(), 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, - (void *)pixel); + QOpenGLContext::currentContext()->functions()->glReadPixels(mousepos.x(), height - mousepos.y(), + 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, + (void *)pixel); QVector4D selectedColor(pixel[0], pixel[1], pixel[2], pixel[3]); return selectedColor; } @@ -322,4 +313,41 @@ QQuaternion Utils::calculateRotation(const QVector3D &xyzRotations) return totalRotation; } +bool Utils::isOpenGLES() +{ +#if defined(QT_OPENGL_ES_2) + return true; +#elif (QT_VERSION < QT_VERSION_CHECK(5, 3, 0)) + return false; +#else + static bool resolved = false; + static bool isES = false; + if (!resolved) { + QOpenGLContext *ctx = QOpenGLContext::currentContext(); + QWindow *dummySurface = 0; + if (!ctx) { + QSurfaceFormat surfaceFormat = qDefaultSurfaceFormat(); + dummySurface = new QWindow(); + dummySurface->setSurfaceType(QWindow::OpenGLSurface); + dummySurface->setFormat(surfaceFormat); + dummySurface->create(); + ctx = new QOpenGLContext; + ctx->setFormat(surfaceFormat); + ctx->create(); + ctx->makeCurrent(dummySurface); + } + + isES = ctx->isOpenGLES(); + resolved = true; + + if (dummySurface) { + ctx->doneCurrent(); + delete ctx; + delete dummySurface; + } + } + return isES; +#endif +} + QT_END_NAMESPACE_DATAVISUALIZATION diff --git a/src/datavisualization/utils/utils_p.h b/src/datavisualization/utils/utils_p.h index fdcb0da0..1a46c731 100644 --- a/src/datavisualization/utils/utils_p.h +++ b/src/datavisualization/utils/utils_p.h @@ -69,6 +69,7 @@ public: static float wrapValue(float value, float min, float max); static QQuaternion calculateRotation(const QVector3D &xyzRotations); + static bool isOpenGLES(); private: static ParamType mapFormatCharToParamType(char formatSpec); diff --git a/src/datavisualizationqml2/abstractdeclarative.cpp b/src/datavisualizationqml2/abstractdeclarative.cpp index b668c08f..51c254ec 100644 --- a/src/datavisualizationqml2/abstractdeclarative.cpp +++ b/src/datavisualizationqml2/abstractdeclarative.cpp @@ -19,7 +19,6 @@ #include "abstractdeclarative_p.h" #include "declarativetheme_p.h" #include "declarativerendernode_p.h" - #include #if defined(Q_OS_IOS) #include @@ -39,11 +38,7 @@ AbstractDeclarative::AbstractDeclarative(QQuickItem *parent) : m_controller(0), m_contextWindow(0), m_renderMode(RenderIndirect), - #if defined(QT_OPENGL_ES_2) m_samples(0), - #else - m_samples(4), - #endif m_windowSamples(0), m_initialisedSize(0, 0), #ifdef USE_SHARED_CONTEXT @@ -56,7 +51,6 @@ AbstractDeclarative::AbstractDeclarative(QQuickItem *parent) : m_contextThread(0) { connect(this, &QQuickItem::windowChanged, this, &AbstractDeclarative::handleWindowChanged); - setAntialiasing(m_samples > 0); // Set contents to false in case we are in qml designer to make component look nice m_runningInDesigner = QGuiApplication::applicationDisplayName() == "Qml2Puppet"; @@ -279,6 +273,10 @@ void AbstractDeclarative::setSharedController(Abstract3DController *controller) Q_ASSERT(controller); m_controller = controller; + if (!m_controller->isOpenGLES()) + m_samples = 4; + setAntialiasing(m_samples > 0); + // Reset default theme, as the default C++ theme is Q3DTheme, not DeclarativeTheme3D. DeclarativeTheme3D *defaultTheme = new DeclarativeTheme3D; defaultTheme->d_ptr->setDefaultTheme(true); @@ -425,17 +423,15 @@ void AbstractDeclarative::setMsaaSamples(int samples) if (m_renderMode != RenderIndirect) { qWarning("Multisampling cannot be adjusted in this render mode"); } else { -#if defined(QT_OPENGL_ES_2) - if (samples > 0) - qWarning("Multisampling is not supported in OpenGL ES2"); -#else - if (m_samples != samples) { + if (m_controller->isOpenGLES()) { + if (samples > 0) + qWarning("Multisampling is not supported in OpenGL ES2"); + } else if (m_samples != samples) { m_samples = samples; setAntialiasing(m_samples > 0); emit msaaSamplesChanged(samples); update(); } -#endif } } @@ -577,24 +573,25 @@ void AbstractDeclarative::render() // Clear the background once per window as that is not done by default QQuickWindow *win = window(); activateOpenGLContext(win); + QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions(); if (m_renderMode == RenderDirectToBackground && !clearList.contains(win)) { clearList.append(win); QColor clearColor = win->color(); - glClearColor(clearColor.redF(), clearColor.greenF(), clearColor.blueF(), 1.0f); - glClear(GL_COLOR_BUFFER_BIT); + funcs->glClearColor(clearColor.redF(), clearColor.greenF(), clearColor.blueF(), 1.0f); + funcs->glClear(GL_COLOR_BUFFER_BIT); } if (isVisible()) { - glDepthMask(GL_TRUE); - glEnable(GL_DEPTH_TEST); - glDepthFunc(GL_LESS); - glEnable(GL_CULL_FACE); - glCullFace(GL_BACK); - glDisable(GL_BLEND); + funcs->glDepthMask(GL_TRUE); + funcs->glEnable(GL_DEPTH_TEST); + funcs->glDepthFunc(GL_LESS); + funcs->glEnable(GL_CULL_FACE); + funcs->glCullFace(GL_BACK); + funcs->glDisable(GL_BLEND); m_controller->render(); - glEnable(GL_BLEND); + funcs->glEnable(GL_BLEND); } doneOpenGLContext(win); } diff --git a/tests/barstest/main.cpp b/tests/barstest/main.cpp index 9a791c5b..af033990 100644 --- a/tests/barstest/main.cpp +++ b/tests/barstest/main.cpp @@ -34,6 +34,18 @@ #include #include #include +#include + +static bool isOpenGLES() +{ +#if defined(QT_OPENGL_ES_2) + return true; +#elif (QT_VERSION < QT_VERSION_CHECK(5, 3, 0)) + return false; +#else + return QOpenGLContext::currentContext()->isOpenGLES(); +#endif +} int main(int argc, char **argv) { @@ -48,9 +60,8 @@ int main(int argc, char **argv) // For testing custom surface format QSurfaceFormat surfaceFormat; surfaceFormat.setDepthBufferSize(24); -#if !defined(QT_OPENGL_ES_2) - surfaceFormat.setSamples(8); -#endif + if (!isOpenGLES()) + surfaceFormat.setSamples(8); Q3DBars *widgetchart = new Q3DBars(&surfaceFormat); QSize screenSize = widgetchart->screen()->size(); diff --git a/tests/scattertest/main.cpp b/tests/scattertest/main.cpp index 811c7f3b..9b6f51ac 100644 --- a/tests/scattertest/main.cpp +++ b/tests/scattertest/main.cpp @@ -36,6 +36,7 @@ int main(int argc, char **argv) { QApplication app(argc, argv); + //QCoreApplication::setAttribute(Qt::AA_UseOpenGLES); QWidget *widget = new QWidget; QHBoxLayout *hLayout = new QHBoxLayout(widget); @@ -484,7 +485,7 @@ int main(int argc, char **argv) chart->setGeometry(QRect(0, 0, 800, 800)); modifier->start(); - modifier->renderToImage(); // Initial hidden render + //modifier->renderToImage(); // Initial hidden render widget->show(); -- cgit v1.2.3