/**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Data Visualization module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:GPL$ ** 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 The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 or (at your option) any later version ** approved by the KDE Free Qt Foundation. The licenses are as published by ** the Free Software Foundation and appearing in the file LICENSE.GPL3 ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qcustom3ditem_p.h" QT_BEGIN_NAMESPACE_DATAVISUALIZATION /*! * \class QCustom3DItem * \inmodule QtDataVisualization * \brief The QCustom3DItem class is for creating custom items to be added to a graph. * \since QtDataVisualization 1.1 * * This class is for creating custom items to be added to a graph. The item has a custom mesh, * position, scaling, rotation, and an optional texture. * * \sa QAbstract3DGraph::addCustomItem() */ /*! * \qmltype Custom3DItem * \inqmlmodule QtDataVisualization * \since QtDataVisualization 1.1 * \ingroup datavisualization_qml * \instantiates QCustom3DItem * \brief The Custom3DItem type is for creating custom items to be added to a graph. * * This type is for creating custom items to be added to a graph. The item has a custom mesh, * position, scaling, rotation, and an optional texture. */ /*! \qmlproperty string Custom3DItem::meshFile * * Holds item mesh file name. Item in the file must be in Wavefront obj format and include * vertices, normals, and UVs. It also needs to be in triangles. */ /*! \qmlproperty string Custom3DItem::textureFile * * Holds the texture file name for the item. If left unset, a solid gray texture will be * used. * * \note To conserve memory the QImage loaded from the file is cleared after a texture is created. */ /*! \qmlproperty vector3d Custom3DItem::position * * 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 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}, * 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, scalingAbsolute */ /*! \qmlproperty bool Custom3DItem::positionAbsolute * * This property dictates if item position is to be handled in data coordinates or in absolute * coordinates. Defaults to \c{false}. Items with absolute coordinates will always be rendered, * whereas items with data coordinates are only rendered if they are within axis ranges. * * \sa position */ /*! \qmlproperty vector3d Custom3DItem::scaling * * Holds the item \a scaling as a vector3d. Defaults to \c {vector3d(0.1, 0.1, 0.1)}. * * 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 * * Holds the item \a rotation as a quaternion. Defaults to \c {quaternion(0.0, 0.0, 0.0, 0.0)}. */ /*! \qmlproperty bool Custom3DItem::visible * * Sets the item \a visible. Defaults to \c{true}. */ /*! \qmlproperty bool Custom3DItem::shadowCasting * * Sets shadow casting for the item to \a enabled. Defaults to \c{true}. * If set \c{false}, the item does not cast shadows regardless of * \l{QAbstract3DGraph::ShadowQuality}{ShadowQuality}. */ /*! * \qmlmethod void Custom3DItem::setRotationAxisAndAngle(vector3d axis, real angle) * * A convenience function to construct rotation quaternion from \a axis and \a angle. * * \sa rotation */ /*! * Constructs QCustom3DItem with given \a parent. */ QCustom3DItem::QCustom3DItem(QObject *parent) : QObject(parent), d_ptr(new QCustom3DItemPrivate(this)) { setTextureImage(QImage()); } /*! * \internal */ QCustom3DItem::QCustom3DItem(QCustom3DItemPrivate *d, QObject *parent) : QObject(parent), d_ptr(d) { setTextureImage(QImage()); } /*! * Constructs QCustom3DItem with given \a meshFile, \a position, \a scaling, * \a rotation, \a texture image, and optional \a parent. */ QCustom3DItem::QCustom3DItem(const QString &meshFile, const QVector3D &position, const QVector3D &scaling, const QQuaternion &rotation, const QImage &texture, QObject *parent) : QObject(parent), d_ptr(new QCustom3DItemPrivate(this, meshFile, position, scaling, rotation)) { setTextureImage(texture); } /*! * Destroys QCustom3DItem. */ QCustom3DItem::~QCustom3DItem() { } /*! \property QCustom3DItem::meshFile * * Holds item mesh file name. Item in the file must be in Wavefront obj format and include * vertices, normals, and UVs. It also needs to be in triangles. */ void QCustom3DItem::setMeshFile(const QString &meshFile) { if (d_ptr->m_meshFile != meshFile) { d_ptr->m_meshFile = meshFile; d_ptr->m_dirtyBits.meshDirty = true; emit meshFileChanged(meshFile); emit d_ptr->needUpdate(); } } QString QCustom3DItem::meshFile() const { return d_ptr->m_meshFile; } /*! \property QCustom3DItem::position * * Holds the item \a position as a QVector3D. Defaults to \c {QVector3D(0.0, 0.0, 0.0)}. * * Item position is either in data coordinates or in absolute coordinates, depending on * 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}, * 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 */ void QCustom3DItem::setPosition(const QVector3D &position) { if (d_ptr->m_position != position) { d_ptr->m_position = position; d_ptr->m_dirtyBits.positionDirty = true; emit positionChanged(position); emit d_ptr->needUpdate(); } } QVector3D QCustom3DItem::position() const { return d_ptr->m_position; } /*! \property QCustom3DItem::positionAbsolute * * This property dictates if item position is to be handled in data coordinates or in absolute * coordinates. Defaults to \c{false}. Items with absolute coordinates will always be rendered, * whereas items with data coordinates are only rendered if they are within axis ranges. * * \sa position */ void QCustom3DItem::setPositionAbsolute(bool positionAbsolute) { if (d_ptr->m_positionAbsolute != positionAbsolute) { d_ptr->m_positionAbsolute = positionAbsolute; d_ptr->m_dirtyBits.positionDirty = true; emit positionAbsoluteChanged(positionAbsolute); emit d_ptr->needUpdate(); } } bool QCustom3DItem::isPositionAbsolute() const { return d_ptr->m_positionAbsolute; } /*! \property QCustom3DItem::scaling * * Holds the item \a scaling as a QVector3D. Defaults to \c {QVector3D(0.1, 0.1, 0.1)}. * * 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) { if (d_ptr->m_scaling != scaling) { d_ptr->m_scaling = scaling; d_ptr->m_dirtyBits.scalingDirty = true; emit scalingChanged(scaling); emit d_ptr->needUpdate(); } } 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)}. */ void QCustom3DItem::setRotation(const QQuaternion &rotation) { if (d_ptr->m_rotation != rotation) { d_ptr->m_rotation = rotation; d_ptr->m_dirtyBits.rotationDirty = true; emit rotationChanged(rotation); emit d_ptr->needUpdate(); } } QQuaternion QCustom3DItem::rotation() { return d_ptr->m_rotation; } /*! \property QCustom3DItem::visible * * Sets the item \a visible. Defaults to \c{true}. */ void QCustom3DItem::setVisible(bool visible) { if (d_ptr->m_visible != visible) { d_ptr->m_visible = visible; d_ptr->m_dirtyBits.visibleDirty = true; emit visibleChanged(visible); emit d_ptr->needUpdate(); } } bool QCustom3DItem::isVisible() const { return d_ptr->m_visible; } /*! \property QCustom3DItem::shadowCasting * * Sets shadow casting for the item to \a enabled. Defaults to \c{true}. * If set \c{false}, the item does not cast shadows regardless of QAbstract3DGraph::ShadowQuality. */ void QCustom3DItem::setShadowCasting(bool enabled) { if (d_ptr->m_shadowCasting != enabled) { d_ptr->m_shadowCasting = enabled; d_ptr->m_dirtyBits.shadowCastingDirty = true; emit shadowCastingChanged(enabled); emit d_ptr->needUpdate(); } } bool QCustom3DItem::isShadowCasting() const { return d_ptr->m_shadowCasting; } /*! * A convenience function to construct rotation quaternion from \a axis and \a angle. * * \sa rotation */ void QCustom3DItem::setRotationAxisAndAngle(const QVector3D &axis, float angle) { setRotation(QQuaternion::fromAxisAndAngle(axis, angle)); } /*! * Set the \a textureImage as a QImage for the item. Texture defaults to solid gray. * * \note To conserve memory the given QImage is cleared after a texture is created. */ void QCustom3DItem::setTextureImage(const QImage &textureImage) { if (textureImage != d_ptr->m_textureImage) { if (textureImage.isNull()) { // Make a solid gray texture d_ptr->m_textureImage = QImage(2, 2, QImage::Format_RGB32); d_ptr->m_textureImage.fill(Qt::gray); } else { d_ptr->m_textureImage = textureImage; } if (!d_ptr->m_textureFile.isEmpty()) { d_ptr->m_textureFile.clear(); emit textureFileChanged(d_ptr->m_textureFile); } d_ptr->m_dirtyBits.textureDirty = true; emit d_ptr->needUpdate(); } } /*! \property QCustom3DItem::textureFile * * Holds the texture file name for the item. If both this and texture image are unset, a solid * gray texture will be used. * * \note To conserve memory the QImage loaded from the file is cleared after a texture is created. */ void QCustom3DItem::setTextureFile(const QString &textureFile) { if (d_ptr->m_textureFile != textureFile) { d_ptr->m_textureFile = textureFile; if (!textureFile.isEmpty()) { d_ptr->m_textureImage = QImage(textureFile); } else { d_ptr->m_textureImage = QImage(2, 2, QImage::Format_RGB32); d_ptr->m_textureImage.fill(Qt::gray); } emit textureFileChanged(textureFile); d_ptr->m_dirtyBits.textureDirty = true; emit d_ptr->needUpdate(); } } QString QCustom3DItem::textureFile() const { return d_ptr->m_textureFile; } 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_scalingAbsolute(true), m_rotation(identityQuaternion), m_visible(true), m_shadowCasting(true), m_isLabelItem(false), m_isVolumeItem(false) { } QCustom3DItemPrivate::QCustom3DItemPrivate(QCustom3DItem *q, const QString &meshFile, 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_isVolumeItem(false) { } QCustom3DItemPrivate::~QCustom3DItemPrivate() { } QImage QCustom3DItemPrivate::textureImage() { return m_textureImage; } void QCustom3DItemPrivate::clearTextureImage() { m_textureImage = QImage(); m_textureFile.clear(); } void QCustom3DItemPrivate::resetDirtyBits() { m_dirtyBits.textureDirty = false; m_dirtyBits.meshDirty = false; m_dirtyBits.positionDirty = false; m_dirtyBits.scalingDirty = false; m_dirtyBits.rotationDirty = false; m_dirtyBits.visibleDirty = false; m_dirtyBits.shadowCastingDirty = false; } QT_END_NAMESPACE_DATAVISUALIZATION