summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRĂ©mi Benoit <remi.benoit@kdab.com>2015-03-10 10:59:12 +0100
committerPaul Lemire <paul.lemire@kdab.com>2015-03-24 14:00:49 +0000
commit5bc470958f7feabd125aa6ba6e7c487da821cb0b (patch)
treef21c770fb75ed1e80fde51a2284a73445518ee7d
parent61a7b5b5ba8447cd98a00f036c951763e32cbda6 (diff)
Add QRay3D
Change-Id: I5e45f6275c69ea38866086be61a1390bbb31cb9d Reviewed-by: Sean Harmer <sean.harmer@kdab.com>
-rw-r--r--src/core/core.pri4
-rw-r--r--src/core/qray3d.cpp358
-rw-r--r--src/core/qray3d.h104
-rw-r--r--tests/auto/core/core.pro3
-rw-r--r--tests/auto/core/qray3d/qray3d.pro8
-rw-r--r--tests/auto/core/qray3d/tst_qray3d.cpp546
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"