// Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "q3dcamera_p.h" #include "utils_p.h" #include QT_BEGIN_NAMESPACE /*! * \class Q3DCamera * \inmodule QtDataVisualization * \brief Representation of a camera in 3D space. * \since QtDataVisualization 1.0 * * Q3DCamera represents a basic orbit around centerpoint 3D camera that is used when rendering the * data visualization. The class offers simple methods for rotating the camera around the origin * and setting zoom level. */ /*! * \enum Q3DCamera::CameraPreset * * Predefined positions for camera. * * \value CameraPresetNone * Used to indicate a preset has not been set, or the scene has been rotated freely. * \value CameraPresetFrontLow * \value CameraPresetFront * \value CameraPresetFrontHigh * \value CameraPresetLeftLow * \value CameraPresetLeft * \value CameraPresetLeftHigh * \value CameraPresetRightLow * \value CameraPresetRight * \value CameraPresetRightHigh * \value CameraPresetBehindLow * \value CameraPresetBehind * \value CameraPresetBehindHigh * \value CameraPresetIsometricLeft * \value CameraPresetIsometricLeftHigh * \value CameraPresetIsometricRight * \value CameraPresetIsometricRightHigh * \value CameraPresetDirectlyAbove * \value CameraPresetDirectlyAboveCW45 * \value CameraPresetDirectlyAboveCCW45 * \value CameraPresetFrontBelow * In Q3DBars from CameraPresetFrontBelow onward these only work for graphs including negative * values. They act as Preset...Low for positive-only values. * \value CameraPresetLeftBelow * \value CameraPresetRightBelow * \value CameraPresetBehindBelow * \value CameraPresetDirectlyBelow * Acts as CameraPresetFrontLow for positive-only bars. */ /*! * \qmltype Camera3D * \inqmlmodule QtDataVisualization * \since QtDataVisualization 1.0 * \ingroup datavisualization_qml * \instantiates Q3DCamera * \brief Representation of a camera in 3D space. * * Camera3D represents a basic orbit around centerpoint 3D camera that is used when rendering the * data visualization. The type offers simple methods for rotating the camera around the origin * and setting zoom level. * * For Camera3D enums, see \l{Q3DCamera::CameraPreset}. */ /*! * \qmlproperty float Camera3D::xRotation * * The X-rotation angle of the camera around the target point in degrees * starting from the current base position. */ /*! * \qmlproperty float Camera3D::yRotation * * The Y-rotation angle of the camera around the target point in degrees * starting from the current base position. */ /*! * \qmlproperty Camera3D.CameraPreset Camera3D::cameraPreset * * The currently active camera preset, which is one of * \l{Q3DCamera::CameraPreset}{Camera3D.CameraPreset}. If no preset is active, the value * is \l{Q3DCamera::CameraPresetNone}{Camera3D.CameraPresetNone}. */ /*! * \qmlproperty float Camera3D::zoomLevel * * The camera zoom level in percentage. The default value of \c{100.0} * means there is no zoom in or out set in the camera. * The value is limited by the minZoomLevel and maxZoomLevel properties. * * \sa minZoomLevel, maxZoomLevel */ /*! * \qmlproperty float Camera3D::minZoomLevel * * Sets the minimum allowed camera zoom level. * If the new minimum level is higher than the existing maximum level, the maximum level is * adjusted to the new minimum as well. * If the current zoomLevel is outside the new bounds, it is adjusted as well. * The minZoomLevel cannot be set below \c{1.0}. * Defaults to \c{10.0}. * * \sa zoomLevel, maxZoomLevel */ /*! * \qmlproperty float Camera3D::maxZoomLevel * * Sets the maximum allowed camera zoom level. * If the new maximum level is lower than the existing minimum level, the minimum level is * adjusted to the new maximum as well. * If the current zoomLevel is outside the new bounds, it is adjusted as well. * Defaults to \c{500.0f}. * * \sa zoomLevel, minZoomLevel */ /*! * \qmlproperty bool Camera3D::wrapXRotation * * The behavior of the minimum and maximum limits in the X-rotation. * By default, the X-rotation wraps from minimum value to maximum and from * maximum to minimum. * * If set to \c true, the X-rotation of the camera is wrapped from minimum to * maximum and from maximum to minimum. If set to \c false, the X-rotation of * the camera is limited to the sector determined by the minimum and maximum * values. */ /*! * \qmlproperty bool Camera3D::wrapYRotation * * The behavior of the minimum and maximum limits in the Y-rotation. * By default, the Y-rotation is limited between the minimum and maximum values * without any wrapping. * * If \c true, the Y-rotation of the camera is wrapped from minimum to maximum * and from maximum to minimum. If \c false, the Y-rotation of the camera is * limited to the sector determined by the minimum and maximum values. */ /*! * \qmlproperty vector3d Camera3D::target * \since QtDataVisualization 1.2 * * The camera target as a vector3d. Defaults to \c {vector3d(0.0, 0.0, 0.0)}. * * Valid coordinate values are between \c{-1.0...1.0}, where the edge values indicate * the edges of the corresponding axis range. Any values outside this range are clamped to the edge. * * \note For bar graphs, the Y-coordinate is ignored and camera always targets a point on * the horizontal background. */ /*! * Constructs a new 3D camera with position set to origin, up direction facing towards the Y-axis * and looking at origin by default. An optional \a parent parameter can be given and is then passed * to QObject constructor. */ Q3DCamera::Q3DCamera(QObject *parent) : Q3DObject(parent), d_ptr(new Q3DCameraPrivate(this)) { } /*! * Destroys the camera object. */ Q3DCamera::~Q3DCamera() { } /*! * Copies the 3D camera's properties from the given source camera. * Values are copied from the \a source to this object. */ void Q3DCamera::copyValuesFrom(const Q3DObject &source) { // Note: Do not copy values from parent, as we are handling the position internally const Q3DCamera &sourceCamera = static_cast(source); d_ptr->m_requestedTarget = sourceCamera.d_ptr->m_requestedTarget; d_ptr->m_xRotation = sourceCamera.d_ptr->m_xRotation; d_ptr->m_yRotation = sourceCamera.d_ptr->m_yRotation; d_ptr->m_minXRotation = sourceCamera.d_ptr->m_minXRotation; d_ptr->m_minYRotation = sourceCamera.d_ptr->m_minYRotation; d_ptr->m_maxXRotation = sourceCamera.d_ptr->m_maxXRotation; d_ptr->m_maxYRotation = sourceCamera.d_ptr->m_maxYRotation; d_ptr->m_wrapXRotation = sourceCamera.d_ptr->m_wrapXRotation; d_ptr->m_wrapYRotation = sourceCamera.d_ptr->m_wrapYRotation; d_ptr->m_zoomLevel = sourceCamera.d_ptr->m_zoomLevel; d_ptr->m_minZoomLevel = sourceCamera.d_ptr->m_minZoomLevel; d_ptr->m_maxZoomLevel = sourceCamera.d_ptr->m_maxZoomLevel; d_ptr->m_activePreset = sourceCamera.d_ptr->m_activePreset; } /*! * \property Q3DCamera::xRotation * * \brief The X-rotation angle of the camera around the target point in degrees. */ float Q3DCamera::xRotation() const { return d_ptr->m_xRotation; } void Q3DCamera::setXRotation(float rotation) { if (d_ptr->m_wrapXRotation) { rotation = Utils::wrapValue(rotation, d_ptr->m_minXRotation, d_ptr->m_maxXRotation); } else { rotation = qBound(float(d_ptr->m_minXRotation), float(rotation), float(d_ptr->m_maxXRotation)); } if (d_ptr->m_xRotation != rotation) { d_ptr->setXRotation(rotation); if (d_ptr->m_activePreset != CameraPresetNone) { d_ptr->m_activePreset = CameraPresetNone; setDirty(true); } emit xRotationChanged(d_ptr->m_xRotation); } } /*! * \property Q3DCamera::yRotation * * \brief The Y-rotation angle of the camera around the target point in degrees. */ float Q3DCamera::yRotation() const { return d_ptr->m_yRotation; } void Q3DCamera::setYRotation(float rotation) { if (d_ptr->m_wrapYRotation) { rotation = Utils::wrapValue(rotation, d_ptr->m_minYRotation, d_ptr->m_maxYRotation); } else { rotation = qBound(float(d_ptr->m_minYRotation), float(rotation), float(d_ptr->m_maxYRotation)); } if (d_ptr->m_yRotation != rotation) { d_ptr->setYRotation(rotation); if (d_ptr->m_activePreset != CameraPresetNone) { d_ptr->m_activePreset = CameraPresetNone; setDirty(true); } emit yRotationChanged(d_ptr->m_yRotation); } } /*! * \property Q3DCamera::cameraPreset * * \brief The currently active camera preset. * * If no CameraPreset value is set, CameraPresetNone is used by default. */ Q3DCamera::CameraPreset Q3DCamera::cameraPreset() const { return d_ptr->m_activePreset; } void Q3DCamera::setCameraPreset(CameraPreset preset) { switch (preset) { case CameraPresetFrontLow: { setXRotation(0.0f); setYRotation(0.0f); break; } case CameraPresetFront: { setXRotation(0.0f); setYRotation(22.5f); break; } case CameraPresetFrontHigh: { setXRotation(0.0f); setYRotation(45.0f); break; } case CameraPresetLeftLow: { setXRotation(90.0f); setYRotation(0.0f); break; } case CameraPresetLeft: { setXRotation(90.0f); setYRotation(22.5f); break; } case CameraPresetLeftHigh: { setXRotation(90.0f); setYRotation(45.0f); break; } case CameraPresetRightLow: { setXRotation(-90.0f); setYRotation(0.0f); break; } case CameraPresetRight: { setXRotation(-90.0f); setYRotation(22.5f); break; } case CameraPresetRightHigh: { setXRotation(-90.0f); setYRotation(45.0f); break; } case CameraPresetBehindLow: { setXRotation(180.0f); setYRotation(0.0f); break; } case CameraPresetBehind: { setXRotation(180.0f); setYRotation(22.5f); break; } case CameraPresetBehindHigh: { setXRotation(180.0f); setYRotation(45.0f); break; } case CameraPresetIsometricLeft: { setXRotation(45.0f); setYRotation(22.5f); break; } case CameraPresetIsometricLeftHigh: { setXRotation(45.0f); setYRotation(45.0f); break; } case CameraPresetIsometricRight: { setXRotation(-45.0f); setYRotation(22.5f); break; } case CameraPresetIsometricRightHigh: { setXRotation(-45.0f); setYRotation(45.0f); break; } case CameraPresetDirectlyAbove: { setXRotation(0.0f); setYRotation(90.0f); break; } case CameraPresetDirectlyAboveCW45: { setXRotation(-45.0f); setYRotation(90.0f); break; } case CameraPresetDirectlyAboveCCW45: { setXRotation(45.0f); setYRotation(90.0f); break; } case CameraPresetFrontBelow: { setXRotation(0.0f); setYRotation(-45.0f); break; } case CameraPresetLeftBelow: { setXRotation(90.0f); setYRotation(-45.0f); break; } case CameraPresetRightBelow: { setXRotation(-90.0f); setYRotation(-45.0f); break; } case CameraPresetBehindBelow: { setXRotation(180.0f); setYRotation(-45.0f); break; } case CameraPresetDirectlyBelow: { setXRotation(0.0f); setYRotation(-90.0f); break; } default: preset = CameraPresetNone; break; } // All presets target the center of the graph setTarget(zeroVector); if (d_ptr->m_activePreset != preset) { d_ptr->m_activePreset = preset; setDirty(true); emit cameraPresetChanged(preset); } } /*! * \property Q3DCamera::zoomLevel * * \brief The camera zoom level in percentage. * * The default value of \c{100.0f} means there is no zoom in or out set in the * camera. The value is limited by the minZoomLevel and maxZoomLevel properties. * * \sa minZoomLevel, maxZoomLevel */ float Q3DCamera::zoomLevel() const { return d_ptr->m_zoomLevel; } void Q3DCamera::setZoomLevel(float zoomLevel) { float newZoomLevel = qBound(d_ptr->m_minZoomLevel, zoomLevel, d_ptr->m_maxZoomLevel); if (d_ptr->m_zoomLevel != newZoomLevel) { d_ptr->m_zoomLevel = newZoomLevel; setDirty(true); emit zoomLevelChanged(newZoomLevel); } } /*! * \property Q3DCamera::minZoomLevel * * \brief The minimum allowed camera zoom level. * * If the minimum level is set to a new value that is higher than the existing * maximum level, the maximum level is adjusted to the new minimum as well. * If the current zoomLevel is outside the new bounds, it is adjusted as well. * The minZoomLevel cannot be set below \c{1.0f}. * Defaults to \c{10.0f}. * * \sa zoomLevel, maxZoomLevel */ float Q3DCamera::minZoomLevel() const { return d_ptr->m_minZoomLevel; } void Q3DCamera::setMinZoomLevel(float zoomLevel) { // Don't allow minimum to be below one, as that can cause zoom to break. float newMinLevel = qMax(zoomLevel, 1.0f); if (d_ptr->m_minZoomLevel != newMinLevel) { d_ptr->m_minZoomLevel = newMinLevel; if (d_ptr->m_maxZoomLevel < newMinLevel) setMaxZoomLevel(newMinLevel); setZoomLevel(d_ptr->m_zoomLevel); setDirty(true); emit minZoomLevelChanged(newMinLevel); } } /*! * \property Q3DCamera::maxZoomLevel * * \brief The maximum allowed camera zoom level. * * If the maximum level is set to a new value that is lower than the existing * minimum level, the minimum level is adjusted to the new maximum as well. * If the current zoomLevel is outside the new bounds, it is adjusted as well. * Defaults to \c{500.0f}. * * \sa zoomLevel, minZoomLevel */ float Q3DCamera::maxZoomLevel() const { return d_ptr->m_maxZoomLevel; } void Q3DCamera::setMaxZoomLevel(float zoomLevel) { // Don't allow maximum to be below one, as that can cause zoom to break. float newMaxLevel = qMax(zoomLevel, 1.0f); if (d_ptr->m_maxZoomLevel != newMaxLevel) { d_ptr->m_maxZoomLevel = newMaxLevel; if (d_ptr->m_minZoomLevel > newMaxLevel) setMinZoomLevel(newMaxLevel); setZoomLevel(d_ptr->m_zoomLevel); setDirty(true); emit maxZoomLevelChanged(newMaxLevel); } } /*! * \property Q3DCamera::wrapXRotation * * \brief The behavior of the minimum and maximum limits in the X-rotation. * * If set to \c true, the X-rotation of the camera is wrapped from minimum to * maximum and from maximum to minimum. If set to \c false, the X-rotation of * the camera is limited to the sector determined by the minimum and maximum * values. Set to \c true by default. */ bool Q3DCamera::wrapXRotation() const { return d_ptr->m_wrapXRotation; } void Q3DCamera::setWrapXRotation(bool isEnabled) { d_ptr->m_wrapXRotation = isEnabled; } /*! * \property Q3DCamera::wrapYRotation * * \brief The behavior of the minimum and maximum limits in the Y-rotation. * * If \c true, the Y-rotation of the camera is wrapped from minimum to maximum * and from maximum to minimum. If \c false, the Y-rotation of the camera is * limited to the sector determined by the minimum and maximum values. * Set to \c true by default. */ bool Q3DCamera::wrapYRotation() const { return d_ptr->m_wrapYRotation; } void Q3DCamera::setWrapYRotation(bool isEnabled) { d_ptr->m_wrapYRotation = isEnabled; } /*! * Utility function that sets the camera rotations and distance.\a horizontal and \a vertical * define the camera rotations to be used. * Optional \a zoom parameter can be given to set the zoom percentage of the camera within * the bounds defined by minZoomLevel and maxZoomLevel properties. */ void Q3DCamera::setCameraPosition(float horizontal, float vertical, float zoom) { setZoomLevel(zoom); setXRotation(horizontal); setYRotation(vertical); } /*! * \property Q3DCamera::target * \since QtDataVisualization 1.2 * * \brief The camera target as a vector or vertex in the 3D space. * * Defaults to \c {QVector3D(0.0, 0.0, 0.0)}. * * Valid coordinate values are between \c{-1.0...1.0}, where the edge values indicate * the edges of the corresponding axis range. Any values outside this range are clamped to the edge. * * \note For bar graphs, the Y-coordinate is ignored and camera always targets a point on * the horizontal background. */ QVector3D Q3DCamera::target() const { return d_ptr->m_requestedTarget; } void Q3DCamera::setTarget(const QVector3D &target) { QVector3D newTarget = target; if (newTarget.x() < -1.0f) newTarget.setX(-1.0f); else if (newTarget.x() > 1.0f) newTarget.setX(1.0f); if (newTarget.y() < -1.0f) newTarget.setY(-1.0f); else if (newTarget.y() > 1.0f) newTarget.setY(1.0f); if (newTarget.z() < -1.0f) newTarget.setZ(-1.0f); else if (newTarget.z() > 1.0f) newTarget.setZ(1.0f); if (d_ptr->m_requestedTarget != newTarget) { if (d_ptr->m_activePreset != CameraPresetNone) d_ptr->m_activePreset = CameraPresetNone; d_ptr->m_requestedTarget = newTarget; setDirty(true); emit targetChanged(newTarget); } } Q3DCameraPrivate::Q3DCameraPrivate(Q3DCamera *q) : q_ptr(q), m_isViewMatrixUpdateActive(true), m_xRotation(0.0f), m_yRotation(0.0f), m_minXRotation(-180.0f), m_minYRotation(0.0f), m_maxXRotation(180.0f), m_maxYRotation(90.0f), m_zoomLevel(100.0f), m_minZoomLevel(10.0f), m_maxZoomLevel(500.0f), m_wrapXRotation(true), m_wrapYRotation(false), m_activePreset(Q3DCamera::CameraPresetNone) { } Q3DCameraPrivate::~Q3DCameraPrivate() { } // Copies changed values from this camera to the other camera. If the other camera had same changes, // those changes are discarded. void Q3DCameraPrivate::sync(Q3DCamera &other) { if (q_ptr->isDirty()) { other.copyValuesFrom(*q_ptr); q_ptr->setDirty(false); other.setDirty(false); } } void Q3DCameraPrivate::setXRotation(const float rotation) { if (m_xRotation != rotation) { m_xRotation = rotation; q_ptr->setDirty(true); } } void Q3DCameraPrivate::setYRotation(const float rotation) { if (m_yRotation != rotation) { m_yRotation = rotation; q_ptr->setDirty(true); } } /*! * \internal * The current minimum X-rotation for the camera. * The full circle range is \c{[-180, 180]} and the minimum value is limited to \c -180. * Also the value can't be higher than the maximum, and is adjusted if necessary. * * \sa wrapXRotation, maxXRotation */ float Q3DCameraPrivate::minXRotation() const { return m_minXRotation; } void Q3DCameraPrivate::setMinXRotation(float minRotation) { minRotation = qBound(-180.0f, minRotation, 180.0f); if (minRotation > m_maxXRotation) minRotation = m_maxXRotation; if (m_minXRotation != minRotation) { m_minXRotation = minRotation; emit minXRotationChanged(minRotation); if (m_xRotation < m_minXRotation) setXRotation(m_xRotation); q_ptr->setDirty(true); } } /*! * \internal * The current minimum Y-rotation for the camera. * The full Y angle range is \c{[-90, 90]} and the minimum value is limited to \c -90. * Also the value can't be higher than the maximum, and is adjusted if necessary. * * \sa wrapYRotation, maxYRotation */ float Q3DCameraPrivate::minYRotation() const { return m_minYRotation; } void Q3DCameraPrivate::setMinYRotation(float minRotation) { minRotation = qBound(-90.0f, minRotation, 90.0f); if (minRotation > m_maxYRotation) minRotation = m_maxYRotation; if (m_minYRotation != minRotation) { m_minYRotation = minRotation; emit minYRotationChanged(minRotation); if (m_yRotation < m_minYRotation) setYRotation(m_yRotation); q_ptr->setDirty(true); } } /*! * \internal * The current maximum X-rotation for the camera. * The full circle range is \c{[-180, 180]} and the maximum value is limited to \c 180. * Also the value can't be lower than the minimum, and is adjusted if necessary. * * \sa wrapXRotation, minXRotation */ float Q3DCameraPrivate::maxXRotation() const { return m_maxXRotation; } void Q3DCameraPrivate::setMaxXRotation(float maxRotation) { maxRotation = qBound(-180.0f, maxRotation, 180.0f); if (maxRotation < m_minXRotation) maxRotation = m_minXRotation; if (m_maxXRotation != maxRotation) { m_maxXRotation = maxRotation; emit maxXRotationChanged(maxRotation); if (m_xRotation > m_maxXRotation) setXRotation(m_xRotation); q_ptr->setDirty(true); } } /*! * \internal * The current maximum Y-rotation for the camera. * The full Y angle range is \c{[-90, 90]} and the maximum value is limited to \c 90. * Also the value can't be lower than the minimum, and is adjusted if necessary. * * \sa wrapYRotation, minYRotation */ float Q3DCameraPrivate::maxYRotation() const { return m_maxYRotation; } void Q3DCameraPrivate::setMaxYRotation(float maxRotation) { maxRotation = qBound(-90.0f, maxRotation, 90.0f); if (maxRotation < m_minYRotation) maxRotation = m_minYRotation; if (m_maxYRotation != maxRotation) { m_maxYRotation = maxRotation; emit maxYRotationChanged(maxRotation); if (m_yRotation > m_maxYRotation) setYRotation(m_yRotation); q_ptr->setDirty(true); } } // Recalculates the view matrix based on the currently set base orientation, rotation and zoom level values. // zoomAdjustment is adjustment to ensure that the 3D visualization stays inside the view area in the 100% zoom. void Q3DCameraPrivate::updateViewMatrix(float zoomAdjustment) { if (!m_isViewMatrixUpdateActive) return; GLfloat zoom = m_zoomLevel * zoomAdjustment; QMatrix4x4 viewMatrix; // Apply to view matrix viewMatrix.lookAt(q_ptr->position(), m_actualTarget, m_up); // Compensate for translation (if d_ptr->m_target is off origin) viewMatrix.translate(m_actualTarget.x(), m_actualTarget.y(), m_actualTarget.z()); // Apply rotations // Handle x and z rotation when y -angle is other than 0 viewMatrix.rotate(m_xRotation, 0, qCos(qDegreesToRadians(m_yRotation)), qSin(qDegreesToRadians(m_yRotation))); // y rotation is always "clean" viewMatrix.rotate(m_yRotation, 1.0f, 0.0f, 0.0f); // handle zoom by scaling viewMatrix.scale(zoom / 100.0f); // Compensate for translation (if d_ptr->m_target is off origin) viewMatrix.translate(-m_actualTarget.x(), -m_actualTarget.y(), -m_actualTarget.z()); setViewMatrix(viewMatrix); } /*! * \internal * The view matrix used in the 3D calculations. When the default orbiting * camera behavior is sufficient, there is no need to touch this property. If the default * behavior is insufficient, the view matrix can be set directly. * \note When setting the view matrix directly remember to set viewMatrixAutoUpdateEnabled to * \c false. */ QMatrix4x4 Q3DCameraPrivate::viewMatrix() const { return m_viewMatrix; } void Q3DCameraPrivate::setViewMatrix(const QMatrix4x4 &viewMatrix) { if (m_viewMatrix != viewMatrix) { m_viewMatrix = viewMatrix; q_ptr->setDirty(true); emit viewMatrixChanged(m_viewMatrix); } } /*! * \internal * This property determines if view matrix is automatically updated each render cycle using the * current base orientation and rotations. If set to \c false, no automatic recalculation is done * and the view matrix can be set using the viewMatrix property. */ bool Q3DCameraPrivate::isViewMatrixAutoUpdateEnabled() const { return m_isViewMatrixUpdateActive; } void Q3DCameraPrivate::setViewMatrixAutoUpdateEnabled(bool isEnabled) { m_isViewMatrixUpdateActive = isEnabled; emit viewMatrixAutoUpdateChanged(isEnabled); } /*! * \internal * Sets the base values for the camera that are used when calculating the camera position using the * rotation values. The base position of the camera is defined by \a basePosition, expectation is * that the x and y values are 0. Look at target point is defined by \a target and the camera * rotates around it. Up direction for the camera is defined by \a baseUp, normally this is a * vector with only y value set to 1. */ void Q3DCameraPrivate::setBaseOrientation(const QVector3D &basePosition, const QVector3D &target, const QVector3D &baseUp) { if (q_ptr->position() != basePosition || m_actualTarget != target || m_up != baseUp) { q_ptr->setPosition(basePosition); m_actualTarget = target; m_up = baseUp; q_ptr->setDirty(true); } } /*! * \internal * Calculates and returns a position relative to the camera using the given parameters * and the current camera viewMatrix property. * The relative 3D offset to the current camera position is defined in \a relativePosition. * An optional fixed rotation of the calculated point around the data visualization area can be * given in \a fixedRotation. The rotation is given in degrees. * An optional \a distanceModifier modifies the distance of the calculated point from the data * visualization. * \return calculated position relative to this camera's position. */ QVector3D Q3DCameraPrivate::calculatePositionRelativeToCamera(const QVector3D &relativePosition, float fixedRotation, float distanceModifier) const { // Move the position with camera const float radiusFactor = cameraDistance * (1.5f + distanceModifier); float xAngle; float yAngle; if (!fixedRotation) { xAngle = qDegreesToRadians(m_xRotation); float yRotation = m_yRotation; // Light must not be paraller to eye vector, so fudge the y rotation a bit. // Note: This needs redoing if we ever allow arbitrary light positioning. const float yMargin = 0.1f; // Smaller margins cause weird shadow artifacts on tops of bars const float absYRotation = qAbs(yRotation); if (absYRotation < 90.0f + yMargin && absYRotation > 90.0f - yMargin) { if (yRotation < 0.0f) yRotation = -90.0f + yMargin; else yRotation = 90.0f - yMargin; } yAngle = qDegreesToRadians(yRotation); } else { xAngle = qDegreesToRadians(fixedRotation); yAngle = 0; } // Set radius to match the highest height of the position const float radius = (radiusFactor + relativePosition.y()); const float zPos = radius * qCos(xAngle) * qCos(yAngle); const float xPos = radius * qSin(xAngle) * qCos(yAngle); const float yPos = radius * qSin(yAngle); // Keep in the set position in relation to camera return QVector3D(-xPos + relativePosition.x(), yPos + relativePosition.y(), zPos + relativePosition.z()); } QT_END_NAMESPACE