diff options
author | RĂ©mi Benoit <remi.benoit@kdab.com> | 2015-03-10 10:59:12 +0100 |
---|---|---|
committer | Paul Lemire <paul.lemire@kdab.com> | 2015-03-24 14:00:49 +0000 |
commit | 5bc470958f7feabd125aa6ba6e7c487da821cb0b (patch) | |
tree | f21c770fb75ed1e80fde51a2284a73445518ee7d | |
parent | 61a7b5b5ba8447cd98a00f036c951763e32cbda6 (diff) |
Add QRay3D
Change-Id: I5e45f6275c69ea38866086be61a1390bbb31cb9d
Reviewed-by: Sean Harmer <sean.harmer@kdab.com>
-rw-r--r-- | src/core/core.pri | 4 | ||||
-rw-r--r-- | src/core/qray3d.cpp | 358 | ||||
-rw-r--r-- | src/core/qray3d.h | 104 | ||||
-rw-r--r-- | tests/auto/core/core.pro | 3 | ||||
-rw-r--r-- | tests/auto/core/qray3d/qray3d.pro | 8 | ||||
-rw-r--r-- | tests/auto/core/qray3d/tst_qray3d.cpp | 546 |
6 files changed, 1021 insertions, 2 deletions
diff --git a/src/core/core.pri b/src/core/core.pri index c44e09c64..32bd30bc9 100644 --- a/src/core/core.pri +++ b/src/core/core.pri @@ -40,6 +40,7 @@ HEADERS += \ $$PWD/qlockableobserverinterface_p.h \ $$PWD/qchangearbiter_p.h \ $$PWD/qbackendnodefactory.h \ + $$PWD/qray3d.h \ $$PWD/qt3dcore_global_p.h SOURCES += \ @@ -57,4 +58,5 @@ SOURCES += \ $$PWD/qpostman.cpp \ $$PWD/qscene.cpp \ $$PWD/qbackendscenepropertychange.cpp \ - $$PWD/qbackendnodefactory.cpp + $$PWD/qbackendnodefactory.cpp \ + $$PWD/qray3d.cpp diff --git a/src/core/qray3d.cpp b/src/core/qray3d.cpp new file mode 100644 index 000000000..0783c5aa1 --- /dev/null +++ b/src/core/qray3d.cpp @@ -0,0 +1,358 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2015 Klaralvdalens Datakonsult AB (KDAB). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qray3d.h" +#include <QtCore/qdebug.h> + +QT_BEGIN_NAMESPACE + +namespace Qt3D { + +/*! + \class QRay3D + \brief The QRay3D class defines a directional line in 3D space extending through an origin point. + \since 5.5 + \ingroup qt3d + \ingroup qt3d::math + + A ray is defined by the origin() point and the direction() vector. + Rays are infinite in length, extending out from origin() in + both directions. If the direction() is zero length, then the + behavior of the class is undefined. + + A ray can be thought of as a one-dimensional co-ordinate system. + If the co-ordinate is \b t then the origin() point is at + \b t = 0, the point origin() + direction() is at \b t = 1, + and the point origin() - direction() is at \b t = -1. + The point() method can be used to obtain the position of a point + within this one-dimensional co-ordinate system. The projectedDistance() + method can be used to convert a point into a value in this + one-dimensional co-ordinate system. +*/ + +/*! + \fn QRay3D::QRay3D() + + Construct a default ray with an origin() of (0, 0, 0) and a + direction() of (0, 0, 1). +*/ +QRay3D::QRay3D() + : m_direction(0.0f, 0.0f, 1.0f) +{ +} + +/*! + \fn QRay3D::QRay3D(const QVector3D &origin, const QVector3D &direction) + + Construct a ray given its defining \a origin and \a direction. The + \a direction does not need to be normalized. + + To construct a ray that passes through two points, use the following: + + \code + QRay3D thruAB(pointA, pointB - pointA); + \endcode +*/ +QRay3D::QRay3D(const QVector3D &origin, const QVector3D &direction) + : m_origin(origin) + , m_direction(direction) +{} + +QRay3D::~QRay3D() +{ +} + +/*! + \fn QVector3D QRay3D::origin() const + + Returns the origin of this ray. The default value is (0, 0, 0). + + \sa setOrigin(), direction() +*/ +QVector3D QRay3D::origin() const +{ + return m_origin; +} + +/*! + \fn void QRay3D::setOrigin(const QVector3D &value) + + Sets the origin point of this ray to \a value. + + \sa origin(), setDirection() + */ +void QRay3D::setOrigin(const QVector3D &value) +{ + m_origin = value; +} + +/*! + \fn QVector3D QRay3D::direction() const + + Returns the direction vector of this ray. The default value is (0, 0, 1). + + \sa setDirection(), origin() +*/ +QVector3D QRay3D::direction() const +{ + return m_direction; +} + +/*! + \fn void QRay3D::setDirection(const QVector3D &direction) + + Sets the direction vector of this ray to \a direction. + + \sa direction(), setOrigin() +*/ +void QRay3D::setDirection(const QVector3D &value) +{ + if (value.isNull()) + return; + + m_direction = value; +} + +QVector3D QRay3D::point(float t) const +{ + return m_origin + t * m_direction; +} + +QRay3D &QRay3D::transform(const QMatrix4x4 &matrix) +{ + m_origin = matrix * m_origin; + m_direction = matrix.mapVector(m_direction); + + return *this; +} + +QRay3D QRay3D::transformed(const QMatrix4x4 &matrix) const +{ + return QRay3D(matrix * m_origin, matrix.mapVector(m_direction)); +} + +bool QRay3D::operator==(const QRay3D &other) const +{ + return m_origin == other.origin() && m_direction == other.direction(); +} + +bool QRay3D::operator!=(const QRay3D &other) const +{ + return !(*this == other); +} + +/*! + Returns true if \a point lies on this ray; false otherwise. +*/ +bool QRay3D::contains(const QVector3D &point) const +{ + QVector3D ppVec(point - m_origin); + if (ppVec.isNull()) // point coincides with origin + return true; + const float dot = QVector3D::dotProduct(ppVec, m_direction); + if (qFuzzyIsNull(dot)) + return false; + return qFuzzyCompare(dot*dot, ppVec.lengthSquared() * m_direction.lengthSquared()); +} + +/*! + Returns true if \a ray lies on this ray; false otherwise. If true, + this implies that the two rays are actually the same, but with + different origin() points or an inverted direction(). +*/ +bool QRay3D::contains(const QRay3D &ray) const +{ + const float dot = QVector3D::dotProduct(m_direction, ray.direction()); + if (!qFuzzyCompare(dot*dot, m_direction.lengthSquared() * ray.direction().lengthSquared())) + return false; + return contains(ray.origin()); +} + +/*! + \fn QVector3D QRay3D::point(float t) const + + Returns the point on the ray defined by moving \a t units + along the ray in the direction of the direction() vector. + Note that \a t may be negative in which case the point returned + will lie behind the origin() point with respect to the + direction() vector. + + The units for \a t are defined by direction(). The return value + is precisely origin() + t * direction(). + + \sa projectedDistance(), distance() +*/ + +/*! + Returns the number of direction() units along the ray from origin() + to \a point. Essentially, this function computes the value t, where + \a point = origin() + t * direction(). If \a point is not on the ray, + then the closest point that is on the ray will be used instead. + + If the return value is positive, then \a point lies in front of + the origin() with respect to the direction() vector. If the return + value is negative, then \a point lies behind the origin() with + respect to the direction() vector. + + \sa point(), project() +*/ +float QRay3D::projectedDistance(const QVector3D &point) const +{ + Q_ASSERT(!m_direction.isNull()); + + return QVector3D::dotProduct(point - m_origin, m_direction) / + m_direction.lengthSquared(); +} + +/*! + Returns the projection of \a vector onto this ray. In the + following diagram, the dotted line is the ray, and V is the + \a vector. The return value will be the vector V': + + \image qray3d-project.png + + \sa projectedDistance() +*/ +QVector3D QRay3D::project(const QVector3D &vector) const +{ + QVector3D norm = m_direction.normalized(); + return QVector3D::dotProduct(vector, norm) * norm; +} + +/*! + Returns the minimum distance from this ray to \a point, or equivalently + the length of a line perpendicular to this ray which passes through + \a point. If \a point is on the ray, then this function will return zero. + + \sa point() +*/ +float QRay3D::distance(const QVector3D &point) const +{ + float t = projectedDistance(point); + return (point - (m_origin + t * m_direction)).length(); +} + +/*! + \fn QRay3D &QRay3D::transform(const QMatrix4x4 &matrix) + + Transforms this ray using \a matrix, replacing origin() and + direction() with the transformed versions. + + \sa transformed() +*/ + +/*! + \fn QRay3D QRay3D::transformed(const QMatrix4x4 &matrix) const + + Returns a new ray that is formed by transforming origin() + and direction() using \a matrix. + + \sa transform() +*/ + +/*! + \fn bool QRay3D::operator==(const QRay3D &other) + + Returns true if this ray is the same as \a other; false otherwise. + + \sa operator!=() +*/ + +/*! + \fn bool QRay3D::operator!=(const QRay3D &other) + + Returns true if this ray is not the same as \a other; false otherwise. + + \sa operator==() +*/ + +/*! + \fn bool qFuzzyCompare(const QRay3D &ray1, const QRay3D &ray2) + \relates QRay3D + + Returns true if \a ray1 and \a ray2 are almost equal; false otherwise. +*/ + +#ifndef QT_NO_DEBUG_STREAM + +QDebug operator<<(QDebug dbg, const QRay3D &ray) +{ + dbg.nospace() << "QRay3D(origin(" + << ray.origin().x() << ", " << ray.origin().y() << ", " + << ray.origin().z() << ") - direction(" + << ray.direction().x() << ", " << ray.direction().y() << ", " + << ray.direction().z() << "))"; + return dbg.space(); +} + +#endif + +#ifndef QT_NO_DATASTREAM + +/*! + \relates QRay3D + + Writes the given \a ray to the given \a stream and returns a + reference to the stream. +*/ +QDataStream &operator<<(QDataStream &stream, const QRay3D &ray) +{ + stream << ray.origin(); + stream << ray.direction(); + return stream; +} + +/*! + \relates QRay3D + + Reads a 3D ray from the given \a stream into the given \a ray + and returns a reference to the stream. +*/ +QDataStream &operator>>(QDataStream &stream, QRay3D &ray) +{ + QVector3D origin, direction; + stream >> origin; + stream >> direction; + ray = QRay3D(origin, direction); + return stream; +} + +#endif // QT_NO_DATASTREAM + +} // namespace Qt3D + +QT_END_NAMESPACE diff --git a/src/core/qray3d.h b/src/core/qray3d.h new file mode 100644 index 000000000..3734e69b6 --- /dev/null +++ b/src/core/qray3d.h @@ -0,0 +1,104 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2015 Klaralvdalens Datakonsult AB (KDAB). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3D_QRAY3D_H +#define QT3D_QRAY3D_H + +#include <Qt3DCore/qt3dcore_global.h> +#include <QtGui/qvector3d.h> +#include <QtGui/qmatrix4x4.h> + +QT_BEGIN_NAMESPACE + +namespace Qt3D { + +class QT3DCORESHARED_EXPORT QRay3D +{ +public: + QRay3D(); + explicit QRay3D(const QVector3D &origin, const QVector3D &direction = QVector3D(0.0f, 0.0f, 1.0f)); + virtual ~QRay3D(); + + QVector3D origin() const; + void setOrigin(const QVector3D &value); + + QVector3D direction() const; + void setDirection(const QVector3D &value); + + bool contains(const QVector3D &point) const; + bool contains(const QRay3D &ray) const; + + QVector3D point(float t) const; + float projectedDistance(const QVector3D &point) const; + + QVector3D project(const QVector3D &vector) const; + + float distance(const QVector3D &point) const; + + QRay3D &transform(const QMatrix4x4 &matrix); + QRay3D transformed(const QMatrix4x4 &matrix) const; + + bool operator==(const QRay3D &other) const; + bool operator!=(const QRay3D &other) const; + +private: + QVector3D m_origin; + QVector3D m_direction; +}; + +#ifndef QT_NO_DEBUG_STREAM +QT3DCORESHARED_EXPORT QDebug operator<<(QDebug dbg, const Qt3D::QRay3D &ray); +#endif + +#ifndef QT_NO_DATASTREAM +QT3DCORESHARED_EXPORT QDataStream &operator<<(QDataStream &stream, const Qt3D::QRay3D &ray); +QT3DCORESHARED_EXPORT QDataStream &operator>>(QDataStream &stream, Qt3D::QRay3D &ray); +#endif + +} // namespace Qt3D + +QT_END_NAMESPACE + +inline bool qFuzzyCompare(const Qt3D::QRay3D &ray1, const Qt3D::QRay3D &ray2) +{ + return qFuzzyCompare(ray1.origin(), ray2.origin()) && + qFuzzyCompare(ray1.direction(), ray2.direction()); +} + +Q_DECLARE_METATYPE(Qt3D::QRay3D) + +#endif // QT3D_QRAY3D_H diff --git a/tests/auto/core/core.pro b/tests/auto/core/core.pro index 6db834a65..39a9bf030 100644 --- a/tests/auto/core/core.pro +++ b/tests/auto/core/core.pro @@ -13,7 +13,8 @@ SUBDIRS = \ qaspectfactory \ qchangearbiter \ qscene \ - qservicelocator + qservicelocator \ + qray3d contains(QT_CONFIG, private_tests) { SUBDIRS += \ diff --git a/tests/auto/core/qray3d/qray3d.pro b/tests/auto/core/qray3d/qray3d.pro new file mode 100644 index 000000000..f86496142 --- /dev/null +++ b/tests/auto/core/qray3d/qray3d.pro @@ -0,0 +1,8 @@ +TARGET = tst_qray3d +CONFIG += testcase +TEMPLATE = app + +SOURCES += \ + tst_qray3d.cpp + +QT += testlib core-private 3dcore 3dcore-private diff --git a/tests/auto/core/qray3d/tst_qray3d.cpp b/tests/auto/core/qray3d/tst_qray3d.cpp new file mode 100644 index 000000000..1f4f829a2 --- /dev/null +++ b/tests/auto/core/qray3d/tst_qray3d.cpp @@ -0,0 +1,546 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2015 Klaralvdalens Datakonsult AB (KDAB). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtTest/QtTest> +#include <Qt3DCore/qray3d.h> + +class tst_QRay3D : public QObject +{ + Q_OBJECT +public: + tst_QRay3D() {} + ~tst_QRay3D() {} + +private Q_SLOTS: + void create_data(); + void create(); + void projection_data(); + void projection(); + void point_data(); + void point(); + void contains_point_data(); + void contains_point(); + void contains_ray_data(); + void contains_ray(); + void distance_data(); + void distance(); + void compare(); + void dataStream(); + void transform_data(); + void transform(); + void properties(); + void metaTypes(); + void shouldNotAllowNullDirection(); +}; + +// Fix the problem where a compared value happens to be zero (and +// you cannot always predict this, and should not predict it +// since then you produce self-fulling prophecies instead of tests). +// In that case qFuzzyCompare has a completely strict criterion since +// it finds the "fudge factor" by multiplying by zero... +static inline bool fuzzyCompare(float p1, float p2) +{ + float fac = qMin(qAbs(p1), qAbs(p2)); + return (qAbs(p1 - p2) <= (qIsNull(fac) ? 0.00001f : 0.00001f * fac)); +} + +static inline bool fuzzyCompare(const QVector3D &lhs, const QVector3D &rhs) +{ + if (fuzzyCompare(lhs.x(), rhs.x()) && + fuzzyCompare(lhs.y(), rhs.y()) && + fuzzyCompare(lhs.z(), rhs.z())) + return true; +#ifndef QT_NO_DEBUG_STREAM + qWarning() << "actual:" << lhs; + qWarning() << "expected:" << rhs; +#endif + return false; +} + +void tst_QRay3D::create_data() +{ + QTest::addColumn<QVector3D>("point"); + QTest::addColumn<QVector3D>("direction"); + + // normalized direction vectors + QTest::newRow("line on x-axis from origin") + << QVector3D() + << QVector3D(1.0f, 0.0f, 0.0f); + + QTest::newRow("line parallel -z-axis from 3,3,3") + << QVector3D(3.0f, 3.0f, 3.0f) + << QVector3D(0.0f, 0.0f, -1.0f); + + QTest::newRow("vertical line (parallel to y-axis)") + << QVector3D(0.5f, 0.0f, 0.5f) + << QVector3D(0.0f, 1.0f, 0.0f); + + QTest::newRow("equidistant from all 3 axes") + << QVector3D(0.5f, 0.0f, 0.5f) + << QVector3D(0.57735026919f, 0.57735026919f, 0.57735026919f); + + // non-normalized direction vectors + QTest::newRow("line on x-axis from origin - B") + << QVector3D() + << QVector3D(2.0f, 0.0f, 0.0f); + + QTest::newRow("line parallel -z-axis from 3,3,3 - B") + << QVector3D(3.0f, 3.0f, 3.0f) + << QVector3D(0.0f, 0.0f, -0.7f); + + QTest::newRow("vertical line (parallel to y-axis) - B") + << QVector3D(0.5f, 0.0f, 0.5f) + << QVector3D(0.0f, 5.3f, 0.0f); + + QTest::newRow("equidistant from all 3 axes - B") + << QVector3D(0.5f, 0.0f, 0.5f) + << QVector3D(1.0f, 1.0f, 1.0f); + + QTest::newRow("negative direction") + << QVector3D(-3.0f, -3.0f, -3.0f) + << QVector3D(-1.2f, -1.8f, -2.4f); +} + +void tst_QRay3D::create() +{ + QFETCH(QVector3D, point); + QFETCH(QVector3D, direction); + Qt3D::QRay3D ray(point, direction); + QVERIFY(fuzzyCompare(ray.direction(), direction)); + QVERIFY(fuzzyCompare(ray.origin(), point)); + + Qt3D::QRay3D ray2; + QCOMPARE(ray2.origin(), QVector3D(0, 0, 0)); + QCOMPARE(ray2.direction(), QVector3D(0, 0, 1)); + ray2.setOrigin(point); + ray2.setDirection(direction); + QVERIFY(fuzzyCompare(ray.direction(), direction)); + QVERIFY(fuzzyCompare(ray.origin(), point)); +} + +void tst_QRay3D::projection_data() +{ + QTest::addColumn<QVector3D>("point"); + QTest::addColumn<QVector3D>("direction"); + QTest::addColumn<QVector3D>("vector"); + QTest::addColumn<QVector3D>("expected"); + + QTest::newRow("line on x-axis from origin") + << QVector3D() + << QVector3D(2.0f, 0.0f, 0.0f) + << QVector3D(0.6f, 0.0f, 0.0f) + << QVector3D(0.6f, 0.0f, 0.0f); + + QTest::newRow("line parallel -z-axis from 3,3,3") + << QVector3D(3.0f, 3.0f, 3.0f) + << QVector3D(0.0f, 0.0f, -0.7f) + << QVector3D(3.0f, 3.0f, 2.4f) + << QVector3D(0.0f, 0.0f, 2.4f); + + QTest::newRow("vertical line (parallel to y-axis)") + << QVector3D(0.5f, 0.0f, 0.5f) + << QVector3D(0.0f, 5.3f, 0.0f) + << QVector3D(0.5f, 0.6f, 0.5f) + << QVector3D(0.0f, 0.6f, 0.0f); + + QTest::newRow("equidistant from all 3 axes, project y-axis (with some z & x)") + << QVector3D(0.5f, 0.0f, 0.5f) + << QVector3D(1.0f, 1.0f, 1.0f) + << QVector3D(0.5f, 5.0f, 0.5f) + << QVector3D(2.0f, 2.0f, 2.0f); + + QTest::newRow("negative direction line, project +ve y-axis (with some z & x)") + << QVector3D(-3.0f, -3.0f, -3.0f) + << QVector3D(-1.2f, -1.8f, -2.4f) + << QVector3D(0.5f, 5.0f, 0.5f) + << QVector3D(1.241379261016846f, 1.862068772315979f, 2.48275852203369f); +} + +void tst_QRay3D::projection() +{ + QFETCH(QVector3D, point); + QFETCH(QVector3D, direction); + QFETCH(QVector3D, vector); + QFETCH(QVector3D, expected); + Qt3D::QRay3D line(point, direction); + QVector3D result = line.project(vector); + QVERIFY(fuzzyCompare(result, expected)); +} + +void tst_QRay3D::point_data() +{ + QTest::addColumn<QVector3D>("point"); + QTest::addColumn<QVector3D>("direction"); + QTest::addColumn<QVector3D>("point_on_line_pos_0_6"); + QTest::addColumn<QVector3D>("point_on_line_neg_7_2"); + + QTest::newRow("line on x-axis from origin") + << QVector3D() + << QVector3D(2.0f, 0.0f, 0.0f) + << QVector3D(1.2f, 0.0f, 0.0f) + << QVector3D(-14.4f, 0.0f, 0.0f); + + QTest::newRow("line parallel -z-axis from 3,3,3") + << QVector3D(3.0f, 3.0f, 3.0f) + << QVector3D(0.0f, 0.0f, -0.7f) + << QVector3D(3.0f, 3.0f, 2.58f) + << QVector3D(3.0f, 3.0f, 8.04f); + + QTest::newRow("vertical line (parallel to y-axis)") + << QVector3D(0.5f, 0.0f, 0.5f) + << QVector3D(0.0f, 5.3f, 0.0f) + << QVector3D(0.5f, 3.18f, 0.5f) + << QVector3D(0.5f, -38.16f, 0.5f); + + QTest::newRow("equidistant from all 3 axes") + << QVector3D(0.5f, 0.0f, 0.5f) + << QVector3D(1.0f, 1.0f, 1.0f) + << QVector3D(1.1f, 0.6f, 1.1f) + << QVector3D(-6.7f, -7.2f, -6.7f); + + QTest::newRow("negative direction") + << QVector3D(-3.0f, -3.0f, -3.0f) + << QVector3D(-1.2f, -1.8f, -2.4f) + << QVector3D(-3.72f, -4.08f, -4.44f) + << QVector3D(5.64f, 9.96f, 14.28f); +} + +void tst_QRay3D::point() +{ + QFETCH(QVector3D, point); + QFETCH(QVector3D, direction); + QFETCH(QVector3D, point_on_line_pos_0_6); + QFETCH(QVector3D, point_on_line_neg_7_2); + Qt3D::QRay3D line(point, direction); + QVERIFY(fuzzyCompare(line.point(0.6), point_on_line_pos_0_6)); + QVERIFY(fuzzyCompare(line.point(-7.2), point_on_line_neg_7_2)); + QVERIFY(fuzzyCompare(line.projectedDistance(point_on_line_pos_0_6), 0.6)); + QVERIFY(fuzzyCompare(line.projectedDistance(point_on_line_neg_7_2), -7.2)); +} + +void tst_QRay3D::contains_point_data() +{ + QTest::addColumn<QVector3D>("origin"); + QTest::addColumn<QVector3D>("direction"); + QTest::addColumn<QVector3D>("point"); + QTest::addColumn<bool>("contains"); + + QTest::newRow("bogus this line with null direction") + << QVector3D(1.0, 3.0, 3.0) + << QVector3D(0.0, 0.0, 0.0) + << QVector3D(1.0, 2.0, 4.0) + << false; + + QTest::newRow("point at the origin") + << QVector3D(0.0, 0.0, 0.0) + << QVector3D(1.0, 3.0, 3.0) + << QVector3D(0.0, 0.0, 0.0) + << true; + + QTest::newRow("close to the origin") + << QVector3D(1.0, 1.0, 1.0) + << QVector3D(1.0, 3.0, 3.0) + << QVector3D(1.0005f, 1.0005f, 1.0) + << false; + + QTest::newRow("45 line line in plane x=1") + << QVector3D(1.0, 3.0, 3.0) + << QVector3D(0.0, -1.0, -1.0) + << QVector3D(1.0, 4.0, 4.0) + << true; + { + // This is to prove that the constructed approach give the + // same results + QVector3D p(1.0, 3.0, 3.0); + QVector3D v(0.0, -1.0, -1.0); + + QTest::newRow("constructed 45 line line in plane x=1") + << p + << v + << p + v + << true; + } + + QTest::newRow("intersection with negative s in plane z=-1") + << QVector3D(1.0f, 2.0f, -1.0f) + << QVector3D(1.0f, 1.0f, 0.0f) + << QVector3D(2.0f, 1.0f, 0.0f) + << false; + + QTest::newRow("45 angled line") + << QVector3D(3.0f, 0.0f, -1.0f) + << QVector3D(1.0f, -1.0f, 1.0f) + << QVector3D(6.0f, -3.0f, 2.0f) + << true; + + { + QVector3D p(-10.0, 3.0, 3.0); + QVector3D v(0.0, 20.0, -1.0); + QTest::newRow("constructed vector close to axis") + << p + << v + << p + v * 3.0 + << true; + } + + { + QVector3D p(1.0, 3.0, 3.0); + QVector3D v(40.0, 500.0, -1.0); + QTest::newRow("constructed larger values close to axis") + << p + << v + << p + v + << true; + } +} + +void tst_QRay3D::contains_point() +{ + QFETCH(QVector3D, origin); + QFETCH(QVector3D, direction); + QFETCH(QVector3D, point); + QFETCH(bool, contains); + + Qt3D::QRay3D line(origin, direction); + QCOMPARE(line.contains(point), contains); +} + +void tst_QRay3D::contains_ray_data() +{ + contains_point_data(); +} + +void tst_QRay3D::contains_ray() +{ + QFETCH(QVector3D, origin); + QFETCH(QVector3D, direction); + QFETCH(QVector3D, point); + QFETCH(bool, contains); + + Qt3D::QRay3D line(origin, direction); + if (contains) { + Qt3D::QRay3D line2(point, direction); + QVERIFY(line.contains(line2)); + QVERIFY(line2.contains(line)); + + // Reversed direction is also contained. + Qt3D::QRay3D line3(point, -direction); + QVERIFY(line.contains(line2)); + QVERIFY(line2.contains(line)); + + // Different direction. + Qt3D::QRay3D line4(point, QVector3D(direction.y(), direction.x(), direction.z())); + QVERIFY(!line.contains(line4)); + QVERIFY(!line4.contains(line)); + } else { + Qt3D::QRay3D line2(point, direction); + QVERIFY(!line.contains(line2)); + QVERIFY(!line2.contains(line)); + } +} + +void tst_QRay3D::distance_data() +{ + QTest::addColumn<QVector3D>("origin"); + QTest::addColumn<QVector3D>("direction"); + QTest::addColumn<QVector3D>("point"); + QTest::addColumn<float>("distance"); + + QTest::newRow("axis-x") + << QVector3D(6.0f, 0.0f, 0.0f) + << QVector3D(1.0f, 0.0f, 0.0f) + << QVector3D(0.0f, 0.0f, 0.0f) + << 0.0f; + + QTest::newRow("axis-x to 1") + << QVector3D(6.0f, 0.0f, 0.0f) + << QVector3D(1.0f, 0.0f, 0.0f) + << QVector3D(0.0f, 1.0f, 0.0f) + << 1.0f; + + QTest::newRow("neg-axis-y") + << QVector3D(0.0f, 6.0f, 0.0f) + << QVector3D(0.0f, -1.5f, 0.0f) + << QVector3D(0.0f, 100.0f, 0.0f) + << 0.0f; + + QTest::newRow("neg-axis-y to 2") + << QVector3D(0.0f, 6.0f, 0.0f) + << QVector3D(0.0f, -1.5f, 0.0f) + << QVector3D(2.0f, 0.0f, 0.0f) + << 2.0f; +} + +void tst_QRay3D::distance() +{ + QFETCH(QVector3D, origin); + QFETCH(QVector3D, direction); + QFETCH(QVector3D, point); + QFETCH(float, distance); + + Qt3D::QRay3D line(origin, direction); + QCOMPARE(line.distance(point), distance); +} + +void tst_QRay3D::compare() +{ + Qt3D::QRay3D ray1(QVector3D(10, 20, 30), QVector3D(-3, -4, -5)); + Qt3D::QRay3D ray2(QVector3D(10, 20, 30), QVector3D(1.5f, 2.0f, 2.5f)); + Qt3D::QRay3D ray3(QVector3D(0, 20, 30), QVector3D(-3, -4, -5)); + QVERIFY(ray1 == ray1); + QVERIFY(!(ray1 != ray1)); + QVERIFY(qFuzzyCompare(ray1, ray1)); + QVERIFY(ray1 != ray2); + QVERIFY(!(ray1 == ray2)); + QVERIFY(!qFuzzyCompare(ray1, ray2)); + QVERIFY(ray1 != ray3); + QVERIFY(!(ray1 == ray3)); + QVERIFY(!qFuzzyCompare(ray1, ray3)); +} + +void tst_QRay3D::dataStream() +{ +#ifndef QT_NO_DATASTREAM + Qt3D::QRay3D ray(QVector3D(1.0f, 2.0f, 3.0f), QVector3D(4.0f, 5.0f, 6.0f)); + + QByteArray data; + { + QDataStream stream(&data, QIODevice::WriteOnly); + stream << ray; + } + + Qt3D::QRay3D ray2; + { + QDataStream stream2(data); + stream2 >> ray2; + } + + QVERIFY(ray == ray2); +#endif +} + +void tst_QRay3D::transform_data() +{ + create_data(); +} + +void tst_QRay3D::transform() +{ + QFETCH(QVector3D, point); + QFETCH(QVector3D, direction); + + QMatrix4x4 m; + m.translate(-1.0f, 2.5f, 5.0f); + m.rotate(45.0f, 1.0f, 1.0f, 1.0f); + m.scale(23.5f); + + Qt3D::QRay3D ray1(point, direction); + Qt3D::QRay3D ray2(ray1); + Qt3D::QRay3D ray3; + + ray1.transform(m); + ray3 = ray2.transformed(m); + + QCOMPARE(ray1.origin(), ray3.origin()); + QCOMPARE(ray1.direction(), ray3.direction()); + + QCOMPARE(ray1.origin(), m * point); + QCOMPARE(ray1.direction(), m.mapVector(direction)); +} + +class tst_QRay3DProperties : public QObject +{ + Q_OBJECT + Q_PROPERTY(Qt3D::QRay3D ray READ ray WRITE setRay) +public: + tst_QRay3DProperties(QObject *parent = 0) : QObject(parent) {} + + Qt3D::QRay3D ray() const { return r; } + void setRay(const Qt3D::QRay3D& value) { r = value; } + +private: + Qt3D::QRay3D r; +}; + +// Test getting and setting properties via the metaobject system. +void tst_QRay3D::properties() +{ + tst_QRay3DProperties obj; + + qRegisterMetaType<Qt3D::QRay3D>(); + + obj.setRay(Qt3D::QRay3D(QVector3D(1, 2, 3), QVector3D(4, 5, 6))); + + Qt3D::QRay3D r = qvariant_cast<Qt3D::QRay3D>(obj.property("ray")); + QCOMPARE(r.origin(), QVector3D(1, 2, 3)); + QCOMPARE(r.direction(), QVector3D(4, 5, 6)); + + obj.setProperty("ray", + qVariantFromValue + (Qt3D::QRay3D(QVector3D(-1, -2, -3), QVector3D(-4, -5, -6)))); + + r = qvariant_cast<Qt3D::QRay3D>(obj.property("ray")); + QCOMPARE(r.origin(), QVector3D(-1, -2, -3)); + QCOMPARE(r.direction(), QVector3D(-4, -5, -6)); +} + +void tst_QRay3D::metaTypes() +{ + int id = qMetaTypeId<Qt3D::QRay3D>(); + QVERIFY(QMetaType::type("Qt3D::QRay3D") == id); + QCOMPARE(QByteArray(QMetaType::typeName(id)), QByteArray("Qt3D::QRay3D")); + QVERIFY(QMetaType::isRegistered(id)); +} + +void tst_QRay3D::shouldNotAllowNullDirection() +{ + // GIVEN + Qt3D::QRay3D ray; + + QCOMPARE(ray.origin(), QVector3D(0, 0, 0)); + QCOMPARE(ray.direction(), QVector3D(0, 0, 1)); + + // WHEN + ray.setDirection(QVector3D(0, 0, 0)); + + // THEN + QCOMPARE(ray.direction(), QVector3D(0, 0, 1)); +} + +QTEST_APPLESS_MAIN(tst_QRay3D) + +#include "tst_qray3d.moc" |