blob: 11df7750230a44f26d9963986d6817d662b651ab [file] [log] [blame]
/****************************************************************************
**
** Copyright (C) 2017 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 adds a custom item to a graph.
* \since QtDataVisualization 1.1
*
* A custom 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 Adds a custom item to a graph.
*
* A custom item has a custom mesh, position, scaling, rotation, and an optional
* texture.
*/
/*! \qmlproperty string Custom3DItem::meshFile
*
* The item mesh file name. The 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
*
* 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
*
* The item position as a \l vector3d type. Defaults to
* \c {vector3d(0.0, 0.0, 0.0)}.
*
* Item position is specified either in data coordinates or in absolute
* coordinates, depending on the value of 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
*
* Defines whether 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
*
* The item scaling as a \l vector3d type. Defaults to
* \c {vector3d(0.1, 0.1, 0.1)}.
*
* Item scaling is specified either in data values or in absolute values,
* depending on the value of 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
* have not been changed from the defaults.
*
* \sa scalingAbsolute
*/
/*! \qmlproperty bool Custom3DItem::scalingAbsolute
* \since QtDataVisualization 1.2
*
* Defines whether 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 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 the range \c{[-1 ,1]}, or the data
* scaling will not be accurate.
*
* \sa scaling, positionAbsolute
*/
/*! \qmlproperty quaternion Custom3DItem::rotation
*
* The item rotation as a \l quaternion. Defaults to
* \c {quaternion(0.0, 0.0, 0.0, 0.0)}.
*/
/*! \qmlproperty bool Custom3DItem::visible
*
* The visibility of the item. Defaults to \c{true}.
*/
/*! \qmlproperty bool Custom3DItem::shadowCasting
*
* Defines whether shadow casting for the item is enabled. Defaults to \c{true}.
* If \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 the rotation quaternion from \a axis and
* \a angle.
*
* \sa rotation
*/
/*!
* Constructs a custom 3D item with the specified \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 a custom 3D item with the specified \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);
}
/*!
* Deletes the custom 3D item.
*/
QCustom3DItem::~QCustom3DItem()
{
}
/*! \property QCustom3DItem::meshFile
*
* \brief The item mesh file name.
*
* The 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
*
* \brief The item position as a QVector3D.
*
* Defaults to \c {QVector3D(0.0, 0.0, 0.0)}.
*
* Item position is specified 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 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
*
* \brief Whether 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
*
* \brief The item 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
* have not 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
*
* \brief Whether 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 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 the 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
*
* \brief The item 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
*
* \brief The visibility of the item.
*
* 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
*
* \brief Whether shadow casting for the item is enabled.
*
* Defaults to \c{true}.
* If \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 the rotation quaternion from \a axis and
* \a angle.
*
* \sa rotation
*/
void QCustom3DItem::setRotationAxisAndAngle(const QVector3D &axis, float angle)
{
setRotation(QQuaternion::fromAxisAndAngle(axis, angle));
}
/*!
* Sets the value of \a textureImage as a QImage for the item. The 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
*
* \brief The texture file name for the item.
*
* If both this property and the 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