diff options
Diffstat (limited to 'src/datavisualization/data')
20 files changed, 1922 insertions, 44 deletions
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 a1c70057..f56ca86e 100644 --- a/src/datavisualization/data/customrenderitem.cpp +++ b/src/datavisualization/data/customrenderitem.cpp @@ -23,9 +23,32 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION CustomRenderItem::CustomRenderItem() : AbstractRenderItem(), m_texture(0), + m_positionAbsolute(false), + m_scalingAbsolute(true), 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_labelItem(false), + m_textureWidth(0), + m_textureHeight(0), + m_textureDepth(0), + m_isVolume(false), + m_textureFormat(QImage::Format_ARGB32), + m_sliceIndexX(-1), + m_sliceIndexY(-1), + m_sliceIndexZ(-1), + m_alphaMultiplier(1.0f), + m_preserveOpacity(true), + m_useHighDefShader(true), + m_drawSlices(false), + m_drawSliceFrames(false) + { } @@ -39,4 +62,47 @@ void CustomRenderItem::setMesh(const QString &meshFile) ObjectHelper::resetObjectHelper(m_renderer, m_object, meshFile); } +void CustomRenderItem::setColorTable(const QVector<QRgb> &colors) +{ + 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); + } + } +} + +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 4fb94276..ad868fac 100644 --- a/src/datavisualization/data/customrenderitem_p.h +++ b/src/datavisualization/data/customrenderitem_p.h @@ -31,6 +31,9 @@ #include "abstractrenderitem_p.h" #include "objecthelper_p.h" +#include <QtGui/QRgb> +#include <QtGui/QImage> +#include <QtGui/QColor> QT_BEGIN_NAMESPACE_DATAVISUALIZATION @@ -48,11 +51,17 @@ 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 const 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 const QVector3D &position() const { return m_position; } + inline void setOrigPosition(const QVector3D &position) { m_origPosition = position; } + 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; } + 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; } @@ -68,14 +77,78 @@ 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; setSliceIndexX(m_sliceIndexX); } + inline int textureWidth() const { return m_textureWidth; } + 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; 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<QVector4D> &colors) { m_colorTable = colors; } + void setColorTable(const QVector<QRgb> &colors); + inline const QVector<QVector4D> &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; + 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; } + 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; } + inline void setUseHighDefShader(bool enable) { m_useHighDefShader = enable; } + inline bool useHighDefShader() const {return m_useHighDefShader; } + 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) GLuint m_texture; QVector3D m_scaling; + QVector3D m_origScaling; QVector3D m_position; - bool m_absolute; + QVector3D m_origPosition; + bool m_positionAbsolute; + bool m_scalingAbsolute; ObjectHelper *m_object; // shared reference bool m_needBlend; bool m_visible; @@ -85,6 +158,32 @@ private: bool m_isFacingCamera; QCustom3DItem *m_item; Abstract3DRenderer *m_renderer; + bool m_labelItem; + + // Volume specific + int m_textureWidth; + int m_textureHeight; + int m_textureDepth; + QVector<QVector4D> m_colorTable; + bool m_isVolume; + QImage::Format m_textureFormat; + int m_sliceIndexX; + int m_sliceIndexY; + int m_sliceIndexZ; + QVector3D m_sliceFractions; + float m_alphaMultiplier; + bool m_preserveOpacity; + bool m_useHighDefShader; + QVector3D m_minBounds; + 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<QCustom3DItem *, CustomRenderItem *> CustomRenderItemArray; diff --git a/src/datavisualization/data/data.pri b/src/datavisualization/data/data.pri index 73f398bf..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,4 +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/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/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/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/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 %<format spec> \li Item value in specified format. + * \li %<format spec> \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<QCategory3DAxis *>(m_controller->axisZ()); QCategory3DAxis *categoryAxisX = static_cast<QCategory3DAxis *>(m_controller->axisX()); QValue3DAxis *valueAxis = static_cast<QValue3DAxis *>(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/data/qcustom3ditem.cpp b/src/datavisualization/data/qcustom3ditem.cpp index efee3b15..b9825732 100644 --- a/src/datavisualization/data/qcustom3ditem.cpp +++ b/src/datavisualization/data/qcustom3ditem.cpp @@ -62,13 +62,15 @@ 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 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 @@ -83,8 +85,33 @@ 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. 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. + * + * \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 +207,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 and scalingAbsolute is also + * \c{false}. In that case, the visible portion of the volume will be rendered. * * \sa positionAbsolute */ @@ -211,7 +240,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 +254,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 +277,42 @@ 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. 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. + * + * \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)}. @@ -367,13 +437,16 @@ 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)), - m_rotation(QQuaternion(0.0f, 0.0f, 0.0f, 0.0f)), + m_scalingAbsolute(true), + m_rotation(identityQuaternion), m_visible(true), m_shadowCasting(true), - m_isLabelItem(false) + m_isLabelItem(false), + m_isVolumeItem(false) { } @@ -381,14 +454,17 @@ 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), m_scaling(scaling), + m_scalingAbsolute(true), m_rotation(rotation), m_visible(true), m_shadowCasting(true), - m_isLabelItem(false) + m_isLabelItem(false), + m_isVolumeItem(false) { } @@ -412,7 +488,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 c1ce5996..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,11 +76,13 @@ public: QVector3D m_position; bool m_positionAbsolute; QVector3D m_scaling; + bool m_scalingAbsolute; QQuaternion m_rotation; bool m_visible; bool m_shadowCasting; bool m_isLabelItem; + bool m_isVolumeItem; QCustomItemDirtyBitField m_dirtyBits; 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 new file mode 100644 index 00000000..f8287a81 --- /dev/null +++ b/src/datavisualization/data/qcustom3dvolume.cpp @@ -0,0 +1,1278 @@ +/**************************************************************************** +** +** 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. + * + * 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. + * + * \note Volumetric objects utilize 3D textures, which are not supported in OpenGL ES2 environments. + * + * \sa QAbstract3DGraph::addCustomItem(), QAbstract3DGraph::orthoProjection, useHighDefShader + */ + +/*! + * \qmltype Custom3DVolume + * \inqmlmodule QtDataVisualization + * \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 + * 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 a QML UI. + * + * \note Volumetric objects are only supported with orthographic projection. + * + * \note Volumetric objects utilize 3D textures, which are not supported in OpenGL ES2 environments. + * + * \sa AbstractGraph3D::orthoProjection, useHighDefShader + */ + +/*! \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 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, 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 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, 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 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, drawSlices, drawSliceFrames + */ + +/*! + * \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 + */ + +/*! + * \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}. + */ + +/*! + * \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. + */ +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, setTextureFormat(), colorTable + */ +QCustom3DVolume::QCustom3DVolume(const QVector3D &position, const QVector3D &scaling, + const QQuaternion &rotation, int textureWidth, + int textureHeight, int textureDepth, + QVector<uchar> *textureData, QImage::Format textureFormat, + const QVector<QRgb> &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, setTextureFormat(), 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, setTextureFormat() + */ +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, setTextureFormat() + */ +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); + setTextureHeight(height); + setTextureDepth(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 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, drawSlices, drawSliceFrames + */ +void QCustom3DVolume::setSliceIndexX(int value) +{ + if (dptr()->m_sliceIndexX != value) { + dptr()->m_sliceIndexX = value; + dptr()->m_dirtyBitsVolume.slicesDirty = 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 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, drawSlices, drawSliceFrames + */ +void QCustom3DVolume::setSliceIndexY(int value) +{ + if (dptr()->m_sliceIndexY != value) { + dptr()->m_sliceIndexY = value; + dptr()->m_dirtyBitsVolume.slicesDirty = 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 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, drawSlices, drawSliceFrames + */ +void QCustom3DVolume::setSliceIndexZ(int value) +{ + if (dptr()->m_sliceIndexZ != value) { + dptr()->m_sliceIndexZ = value; + dptr()->m_dirtyBitsVolume.slicesDirty = 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, setTextureFormat(), QImage::colorTable() + */ +void QCustom3DVolume::setColorTable(const QVector<QRgb> &colors) +{ + if (dptr()->m_colorTable != colors) { + dptr()->m_colorTable = colors; + dptr()->m_dirtyBitsVolume.colorTableDirty = true; + emit colorTableChanged(); + emit dptr()->needUpdate(); + } +} + +QVector<QRgb> 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. The padding bytes should indicate an fully transparent color + * to avoid rendering artifacts. + * + * Defaults to \c{0}. + * + * \sa colorTable, setTextureFormat(), setSubTextureData(), textureDataWidth() + */ +void QCustom3DVolume::setTextureData(QVector<uchar> *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, setTextureFormat() + */ +QVector<uchar> *QCustom3DVolume::createTextureData(const QVector<QImage *> &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<uchar> *newTextureData = new QVector<uchar>; + 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<void *>(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<uchar> *QCustom3DVolume::textureData() const +{ + return dptrc()->m_textureData; +} + +/*! + * 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 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, renderSlice() + */ +void QCustom3DVolume::setSubTextureData(Qt::Axis axis, int index, const uchar *data) +{ + if (data) { + 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 (invalid) { + qWarning() << __FUNCTION__ << "Attempted to set invalid subtexture."; + } else { + 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<const void *>(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 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 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. + * + * \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; + if (dptr()->m_textureFormat == QImage::Format_ARGB32 + && image.format() != QImage::Format_ARGB32) { + convertedImage = image.convertToFormat(QImage::Format_ARGB32); + } else { + convertedImage = image; + } + setSubTextureData(axis, index, convertedImage.bits()); + } else { + qWarning() << __FUNCTION__ << "Invalid image size or format."; + } +} + +// 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. + * + * \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."; + } +} + +/*! + * \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 + * + * 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 preserveOpacity, textureData + */ +void QCustom3DVolume::setAlphaMultiplier(float mult) +{ + 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 { + qWarning() << __FUNCTION__ << "Attempted to set negative multiplier."; + } +} + +float QCustom3DVolume::alphaMultiplier() const +{ + return dptrc()->m_alphaMultiplier; +} + +/*! + * \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(); + } +} + +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; +} + +/*! + * \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. + * + * \return the rendered image of the slice, or a null image if invalid index is specified. + * + * \sa setTextureFormat() + */ +QImage QCustom3DVolume::renderSlice(Qt::Axis axis, int index) +{ + return dptr()->renderSlice(axis, index); +} + +/*! + * \internal + */ +QCustom3DVolumePrivate *QCustom3DVolume::dptr() +{ + return static_cast<QCustom3DVolumePrivate *>(d_ptr.data()); +} + +/*! + * \internal + */ +const QCustom3DVolumePrivate *QCustom3DVolume::dptrc() const +{ + return static_cast<const QCustom3DVolumePrivate *>(d_ptr.data()); +} + +QCustom3DVolumePrivate::QCustom3DVolumePrivate(QCustom3DVolume *q) : + QCustom3DItemPrivate(q), + 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), + m_preserveOpacity(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"); +} + +QCustom3DVolumePrivate::QCustom3DVolumePrivate(QCustom3DVolume *q, const QVector3D &position, + const QVector3D &scaling, + const QQuaternion &rotation, + int textureWidth, int textureHeight, + int textureDepth, QVector<uchar> *textureData, + QImage::Format textureFormat, + const QVector<QRgb> &colorTable) : + QCustom3DItemPrivate(q, QStringLiteral(":/defaultMeshes/barFull"), position, scaling, rotation), + 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), + m_alphaMultiplier(1.0f), + m_preserveOpacity(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; + + if (m_textureWidth < 0) + m_textureWidth = 0; + if (m_textureHeight < 0) + m_textureHeight = 0; + if (m_textureDepth < 0) + m_textureDepth = 0; + + 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.slicesDirty = false; + 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) +{ + 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<uchar> 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 = y - 1; i >= 0; 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<uchar>(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<QRgb> 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<QCustom3DVolume *>(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..a9dfda86 --- /dev/null +++ b/src/datavisualization/data/qcustom3dvolume.h @@ -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 +** +****************************************************************************/ + +#ifndef QCUSTOM3DVOLUME_H +#define QCUSTOM3DVOLUME_H + +#include <QtDataVisualization/qdatavisualizationglobal.h> +#include <QtDataVisualization/QCustom3DItem> +#include <QtGui/QColor> +#include <QtGui/QImage> + +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<QRgb> colorTable READ colorTable WRITE setColorTable NOTIFY colorTableChanged) + Q_PROPERTY(QVector<uchar> *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) + 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: + + explicit QCustom3DVolume(QObject *parent = 0); + explicit QCustom3DVolume(const QVector3D &position, const QVector3D &scaling, + const QQuaternion &rotation, int textureWidth, + int textureHeight, int textureDepth, + QVector<uchar> *textureData, QImage::Format textureFormat, + const QVector<QRgb> &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<QRgb> &colors); + QVector<QRgb> colorTable() const; + + void setTextureData(QVector<uchar> *data); + QVector<uchar> *createTextureData(const QVector<QImage *> &images); + QVector<uchar> *textureData() const; + 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; + + void setAlphaMultiplier(float mult); + float alphaMultiplier() const; + void setPreserveOpacity(bool enable); + bool preserveOpacity() const; + + 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: + 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<uchar> *data); + void textureFormatChanged(QImage::Format format); + 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(); + 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..642d2af5 --- /dev/null +++ b/src/datavisualization/data/qcustom3dvolume_p.h @@ -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 +** +****************************************************************************/ + +// +// 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 slicesDirty : 1; + bool colorTableDirty : 1; + bool textureDataDirty : 1; + bool textureFormatDirty : 1; + bool alphaDirty : 1; + bool shaderDirty : 1; + + QCustomVolumeDirtyBitField() + : textureDimensionsDirty(false), + slicesDirty(false), + colorTableDirty(false), + textureDataDirty(false), + textureFormatDirty(false), + alphaDirty(false), + shaderDirty(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<uchar> *textureData, + QImage::Format textureFormat, const QVector<QRgb> &colorTable); + virtual ~QCustom3DVolumePrivate(); + + void resetDirtyBits(); + QImage renderSlice(Qt::Axis axis, int index); + + 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<QRgb> m_colorTable; + QVector<uchar> *m_textureData; + + float m_alphaMultiplier; + 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: + int multipliedAlphaValue(int alpha); + + friend class QCustom3DVolume; +}; + +QT_END_NAMESPACE_DATAVISUALIZATION + +#endif 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/src/datavisualization/data/qsurface3dseries.cpp b/src/datavisualization/data/qsurface3dseries.cpp index 1107d721..7d4dacfe 100644 --- a/src/datavisualization/data/qsurface3dseries.cpp +++ b/src/datavisualization/data/qsurface3dseries.cpp @@ -141,6 +141,14 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION */ /*! + * \qmlproperty string Surface3DSeries::textureFile + * + * Holds the texture file name for the surface texture. To clear the texture, set empty + * file name. + */ + + +/*! * \enum QSurface3DSeries::DrawFlag * * Drawing mode of the surface. Values of this enumeration can be combined with OR operator. @@ -300,6 +308,57 @@ 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. + */ +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 */ QSurface3DSeriesPrivate *QSurface3DSeries::dptr() @@ -445,4 +504,11 @@ void QSurface3DSeriesPrivate::setDrawMode(QSurface3DSeries::DrawFlags mode) } } +void QSurface3DSeriesPrivate::setTexture(const QImage &texture) +{ + m_texture = texture; + if (static_cast<Surface3DController *>(m_controller)) + static_cast<Surface3DController *>(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/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} */ |