| /**************************************************************************** |
| ** |
| ** Copyright (C) 2016 The Qt Company Ltd. |
| ** Copyright (C) 2015 Klaralvdalens Datakonsult AB (KDAB). |
| ** Contact: https://www.qt.io/licensing/ |
| ** |
| ** This file is part of the Qt3D module of the Qt Toolkit. |
| ** |
| ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ |
| ** 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 as published by the Free Software |
| ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT |
| ** 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 <QtTest/QtTest> |
| #include <Qt3DRender/private/qray3d_p.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 Vector3D &lhs, const Vector3D &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<Vector3D>("point"); |
| QTest::addColumn<Vector3D>("direction"); |
| |
| // normalized direction vectors |
| QTest::newRow("line on x-axis from origin") |
| << Vector3D() |
| << Vector3D(1.0f, 0.0f, 0.0f); |
| |
| QTest::newRow("line parallel -z-axis from 3,3,3") |
| << Vector3D(3.0f, 3.0f, 3.0f) |
| << Vector3D(0.0f, 0.0f, -1.0f); |
| |
| QTest::newRow("vertical line (parallel to y-axis)") |
| << Vector3D(0.5f, 0.0f, 0.5f) |
| << Vector3D(0.0f, 1.0f, 0.0f); |
| |
| QTest::newRow("equidistant from all 3 axes") |
| << Vector3D(0.5f, 0.0f, 0.5f) |
| << Vector3D(0.57735026919f, 0.57735026919f, 0.57735026919f); |
| |
| // non-normalized direction vectors |
| QTest::newRow("line on x-axis from origin - B") |
| << Vector3D() |
| << Vector3D(2.0f, 0.0f, 0.0f).normalized(); |
| |
| QTest::newRow("line parallel -z-axis from 3,3,3 - B") |
| << Vector3D(3.0f, 3.0f, 3.0f) |
| << Vector3D(0.0f, 0.0f, -0.7f).normalized(); |
| |
| QTest::newRow("vertical line (parallel to y-axis) - B") |
| << Vector3D(0.5f, 0.0f, 0.5f) |
| << Vector3D(0.0f, 5.3f, 0.0f).normalized(); |
| |
| QTest::newRow("equidistant from all 3 axes - B") |
| << Vector3D(0.5f, 0.0f, 0.5f) |
| << Vector3D(1.0f, 1.0f, 1.0f).normalized(); |
| |
| QTest::newRow("negative direction") |
| << Vector3D(-3.0f, -3.0f, -3.0f) |
| << Vector3D(-1.2f, -1.8f, -2.4f).normalized(); |
| } |
| |
| void tst_QRay3D::create() |
| { |
| QFETCH(Vector3D, point); |
| QFETCH(Vector3D, direction); |
| Qt3DRender::RayCasting::QRay3D ray(point, direction); |
| QVERIFY(fuzzyCompare(ray.direction(), direction)); |
| QVERIFY(fuzzyCompare(ray.origin(), point)); |
| |
| Qt3DRender::RayCasting::QRay3D ray2; |
| QCOMPARE(ray2.origin(), Vector3D(0, 0, 0)); |
| QCOMPARE(ray2.direction(), Vector3D(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<Vector3D>("point"); |
| QTest::addColumn<Vector3D>("direction"); |
| QTest::addColumn<Vector3D>("vector"); |
| QTest::addColumn<Vector3D>("expected"); |
| |
| QTest::newRow("line on x-axis from origin") |
| << Vector3D() |
| << Vector3D(2.0f, 0.0f, 0.0f) |
| << Vector3D(0.6f, 0.0f, 0.0f) |
| << Vector3D(0.6f, 0.0f, 0.0f); |
| |
| QTest::newRow("line parallel -z-axis from 3,3,3") |
| << Vector3D(3.0f, 3.0f, 3.0f) |
| << Vector3D(0.0f, 0.0f, -0.7f) |
| << Vector3D(3.0f, 3.0f, 2.4f) |
| << Vector3D(0.0f, 0.0f, 2.4f); |
| |
| QTest::newRow("vertical line (parallel to y-axis)") |
| << Vector3D(0.5f, 0.0f, 0.5f) |
| << Vector3D(0.0f, 5.3f, 0.0f) |
| << Vector3D(0.5f, 0.6f, 0.5f) |
| << Vector3D(0.0f, 0.6f, 0.0f); |
| |
| QTest::newRow("equidistant from all 3 axes, project y-axis (with some z & x)") |
| << Vector3D(0.5f, 0.0f, 0.5f) |
| << Vector3D(1.0f, 1.0f, 1.0f) |
| << Vector3D(0.5f, 5.0f, 0.5f) |
| << Vector3D(2.0f, 2.0f, 2.0f); |
| |
| QTest::newRow("negative direction line, project +ve y-axis (with some z & x)") |
| << Vector3D(-3.0f, -3.0f, -3.0f) |
| << Vector3D(-1.2f, -1.8f, -2.4f) |
| << Vector3D(0.5f, 5.0f, 0.5f) |
| << Vector3D(1.241379261016846f, 1.862068772315979f, 2.48275852203369f); |
| } |
| |
| void tst_QRay3D::projection() |
| { |
| QFETCH(Vector3D, point); |
| QFETCH(Vector3D, direction); |
| QFETCH(Vector3D, vector); |
| QFETCH(Vector3D, expected); |
| Qt3DRender::RayCasting::QRay3D line(point, direction); |
| Vector3D result = line.project(vector); |
| QVERIFY(fuzzyCompare(result, expected)); |
| } |
| |
| void tst_QRay3D::point_data() |
| { |
| QTest::addColumn<Vector3D>("point"); |
| QTest::addColumn<Vector3D>("direction"); |
| QTest::addColumn<Vector3D>("point_on_line_pos_0_6"); |
| QTest::addColumn<Vector3D>("point_on_line_neg_7_2"); |
| |
| QTest::newRow("line on x-axis from origin") |
| << Vector3D() |
| << Vector3D(2.0f, 0.0f, 0.0f) |
| << Vector3D(0.6f, 0.0f, 0.0f) |
| << Vector3D(-7.2f, 0.0f, 0.0f); |
| |
| QTest::newRow("line parallel -z-axis from 3,3,3") |
| << Vector3D(3.0f, 3.0f, 3.0f) |
| << Vector3D(0.0f, 0.0f, -0.7f) |
| << Vector3D(3.0f, 3.0f, 2.4f) |
| << Vector3D(3.0f, 3.0f, 10.2f); |
| |
| QTest::newRow("vertical line (parallel to y-axis)") |
| << Vector3D(0.5f, 0.0f, 0.5f) |
| << Vector3D(0.0f, 5.3f, 0.0f) |
| << Vector3D(0.5f, 0.6f, 0.5f) |
| << Vector3D(0.5f, -7.2f, 0.5f); |
| |
| QTest::newRow("equidistant from all 3 axes") |
| << Vector3D(0.5f, 0.0f, 0.5f) |
| << Vector3D(1.0f, 1.0f, 1.0f) |
| << Vector3D(0.84641f, 0.34641f, 0.84641f) |
| << Vector3D(-3.65692f, -4.15692f, -3.65692f); |
| |
| QTest::newRow("negative direction") |
| << Vector3D(-3.0f, -3.0f, -3.0f) |
| << Vector3D(-1.2f, -1.8f, -2.4f) |
| << Vector3D(-3.22283f, -3.33425f, -3.44567f) |
| << Vector3D(-0.325987f, 1.01102f, 2.34803f); |
| } |
| |
| void tst_QRay3D::point() |
| { |
| QFETCH(Vector3D, point); |
| QFETCH(Vector3D, direction); |
| QFETCH(Vector3D, point_on_line_pos_0_6); |
| QFETCH(Vector3D, point_on_line_neg_7_2); |
| Qt3DRender::RayCasting::QRay3D line(point, direction); |
| QVERIFY(fuzzyCompare(line.point(0.6f), point_on_line_pos_0_6)); |
| QVERIFY(fuzzyCompare(line.point(-7.2f), point_on_line_neg_7_2)); |
| QVERIFY(fuzzyCompare(line.projectedDistance(point_on_line_pos_0_6), 0.6f)); |
| QVERIFY(fuzzyCompare(line.projectedDistance(point_on_line_neg_7_2), -7.2f)); |
| } |
| |
| void tst_QRay3D::contains_point_data() |
| { |
| QTest::addColumn<Vector3D>("origin"); |
| QTest::addColumn<Vector3D>("direction"); |
| QTest::addColumn<Vector3D>("point"); |
| QTest::addColumn<bool>("contains"); |
| |
| QTest::newRow("bogus this line with null direction") |
| << Vector3D(1.0, 3.0, 3.0) |
| << Vector3D(0.0, 0.0, 0.0) |
| << Vector3D(1.0, 2.0, 4.0) |
| << false; |
| |
| QTest::newRow("point at the origin") |
| << Vector3D(0.0, 0.0, 0.0) |
| << Vector3D(1.0, 3.0, 3.0) |
| << Vector3D(0.0, 0.0, 0.0) |
| << true; |
| |
| QTest::newRow("close to the origin") |
| << Vector3D(1.0, 1.0, 1.0) |
| << Vector3D(1.0, 3.0, 3.0) |
| << Vector3D(1.0005f, 1.0005f, 1.0) |
| << false; |
| |
| QTest::newRow("45 line line in plane x=1") |
| << Vector3D(1.0, 3.0, 3.0) |
| << Vector3D(0.0, -1.0, -1.0) |
| << Vector3D(1.0, 4.0, 4.0) |
| << true; |
| { |
| // This is to prove that the constructed approach give the |
| // same results |
| Vector3D p(1.0, 3.0, 3.0); |
| Vector3D 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") |
| << Vector3D(1.0f, 2.0f, -1.0f) |
| << Vector3D(1.0f, 1.0f, 0.0f) |
| << Vector3D(2.0f, 1.0f, 0.0f) |
| << false; |
| |
| QTest::newRow("45 angled line") |
| << Vector3D(3.0f, 0.0f, -1.0f) |
| << Vector3D(1.0f, -1.0f, 1.0f) |
| << Vector3D(6.0f, -3.0f, 2.0f) |
| << true; |
| |
| { |
| Vector3D p(-10.0, 3.0, 3.0); |
| Vector3D v(0.0, 20.0, -1.0); |
| QTest::newRow("constructed vector close to axis") |
| << p |
| << v |
| << p + v * 3.0 |
| << true; |
| } |
| |
| { |
| Vector3D p(1.0, 3.0, 3.0); |
| Vector3D 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(Vector3D, origin); |
| QFETCH(Vector3D, direction); |
| QFETCH(Vector3D, point); |
| QFETCH(bool, contains); |
| |
| Qt3DRender::RayCasting::QRay3D line(origin, direction); |
| QCOMPARE(line.contains(point), contains); |
| } |
| |
| void tst_QRay3D::contains_ray_data() |
| { |
| contains_point_data(); |
| } |
| |
| void tst_QRay3D::contains_ray() |
| { |
| QFETCH(Vector3D, origin); |
| QFETCH(Vector3D, direction); |
| QFETCH(Vector3D, point); |
| QFETCH(bool, contains); |
| |
| Qt3DRender::RayCasting::QRay3D line(origin, direction); |
| if (contains) { |
| Qt3DRender::RayCasting::QRay3D line2(point, direction); |
| QVERIFY(line.contains(line2)); |
| QVERIFY(line2.contains(line)); |
| |
| // Reversed direction is also contained. |
| Qt3DRender::RayCasting::QRay3D line3(point, -direction); |
| QVERIFY(line.contains(line2)); |
| QVERIFY(line2.contains(line)); |
| |
| // Different direction. |
| Qt3DRender::RayCasting::QRay3D line4(point, Vector3D(direction.y(), direction.x(), direction.z())); |
| QVERIFY(!line.contains(line4)); |
| QVERIFY(!line4.contains(line)); |
| } else { |
| Qt3DRender::RayCasting::QRay3D line2(point, direction); |
| QVERIFY(!line.contains(line2)); |
| QVERIFY(!line2.contains(line)); |
| } |
| } |
| |
| void tst_QRay3D::distance_data() |
| { |
| QTest::addColumn<Vector3D>("origin"); |
| QTest::addColumn<Vector3D>("direction"); |
| QTest::addColumn<Vector3D>("point"); |
| QTest::addColumn<float>("distance"); |
| |
| QTest::newRow("axis-x") |
| << Vector3D(6.0f, 0.0f, 0.0f) |
| << Vector3D(1.0f, 0.0f, 0.0f) |
| << Vector3D(0.0f, 0.0f, 0.0f) |
| << 0.0f; |
| |
| QTest::newRow("axis-x to 1") |
| << Vector3D(6.0f, 0.0f, 0.0f) |
| << Vector3D(1.0f, 0.0f, 0.0f) |
| << Vector3D(0.0f, 1.0f, 0.0f) |
| << 1.0f; |
| |
| QTest::newRow("neg-axis-y") |
| << Vector3D(0.0f, 6.0f, 0.0f) |
| << Vector3D(0.0f, -1.5f, 0.0f) |
| << Vector3D(0.0f, 100.0f, 0.0f) |
| << 0.0f; |
| |
| QTest::newRow("neg-axis-y to 2") |
| << Vector3D(0.0f, 6.0f, 0.0f) |
| << Vector3D(0.0f, -1.5f, 0.0f) |
| << Vector3D(2.0f, 0.0f, 0.0f) |
| << 2.0f; |
| } |
| |
| void tst_QRay3D::distance() |
| { |
| QFETCH(Vector3D, origin); |
| QFETCH(Vector3D, direction); |
| QFETCH(Vector3D, point); |
| QFETCH(float, distance); |
| |
| Qt3DRender::RayCasting::QRay3D line(origin, direction); |
| QCOMPARE(line.distance(point), distance); |
| } |
| |
| void tst_QRay3D::compare() |
| { |
| Qt3DRender::RayCasting::QRay3D ray1(Vector3D(10, 20, 30), Vector3D(-3, -4, -5)); |
| Qt3DRender::RayCasting::QRay3D ray2(Vector3D(10, 20, 30), Vector3D(1.5f, 2.0f, 2.5f)); |
| Qt3DRender::RayCasting::QRay3D ray3(Vector3D(0, 20, 30), Vector3D(-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 |
| Qt3DRender::RayCasting::QRay3D ray(Vector3D(1.0f, 2.0f, 3.0f), Vector3D(4.0f, 5.0f, 6.0f)); |
| |
| QByteArray data; |
| { |
| QDataStream stream(&data, QIODevice::WriteOnly); |
| stream << ray; |
| } |
| |
| Qt3DRender::RayCasting::QRay3D ray2; |
| { |
| QDataStream stream2(data); |
| stream2 >> ray2; |
| } |
| |
| QVERIFY(ray == ray2); |
| #endif |
| } |
| |
| void tst_QRay3D::transform_data() |
| { |
| create_data(); |
| } |
| |
| void tst_QRay3D::transform() |
| { |
| QFETCH(Vector3D, point); |
| QFETCH(Vector3D, direction); |
| |
| Matrix4x4 m; |
| { |
| QMatrix4x4 c; |
| c.translate(-1.0f, 2.5f, 5.0f); |
| c.rotate(45.0f, 1.0f, 1.0f, 1.0f); |
| c.scale(23.5f); |
| m = Matrix4x4(c); |
| } |
| |
| Qt3DRender::RayCasting::QRay3D ray1(point, direction); |
| Qt3DRender::RayCasting::QRay3D ray2(ray1); |
| Qt3DRender::RayCasting::QRay3D ray3; |
| |
| ray1.transform(m); |
| ray3 = ray2.transformed(m); |
| |
| QVERIFY(fuzzyCompare(ray1.origin(), ray3.origin())); |
| QVERIFY(fuzzyCompare(ray1.direction(), ray3.direction())); |
| |
| QVERIFY(fuzzyCompare(ray1.origin(), m * point)); |
| QVERIFY(fuzzyCompare(ray1.direction(), m.mapVector(direction).normalized())); |
| } |
| |
| class tst_QRay3DProperties : public QObject |
| { |
| Q_OBJECT |
| Q_PROPERTY(Qt3DRender::RayCasting::QRay3D ray READ ray WRITE setRay) |
| public: |
| tst_QRay3DProperties(QObject *parent = 0) : QObject(parent) {} |
| |
| Qt3DRender::RayCasting::QRay3D ray() const { return r; } |
| void setRay(const Qt3DRender::RayCasting::QRay3D& value) { r = value; } |
| |
| private: |
| Qt3DRender::RayCasting::QRay3D r; |
| }; |
| |
| // Test getting and setting properties via the metaobject system. |
| void tst_QRay3D::properties() |
| { |
| tst_QRay3DProperties obj; |
| |
| qRegisterMetaType<Qt3DRender::RayCasting::QRay3D>(); |
| |
| obj.setRay(Qt3DRender::RayCasting::QRay3D(Vector3D(1, 2, 3), Vector3D(4, 5, 6))); |
| |
| Qt3DRender::RayCasting::QRay3D r = qvariant_cast<Qt3DRender::RayCasting::QRay3D>(obj.property("ray")); |
| QCOMPARE(r.origin(), Vector3D(1, 2, 3)); |
| QCOMPARE(r.direction(), Vector3D(4, 5, 6).normalized()); |
| |
| obj.setProperty("ray", |
| QVariant::fromValue |
| (Qt3DRender::RayCasting::QRay3D(Vector3D(-1, -2, -3), Vector3D(-4, -5, -6)))); |
| |
| r = qvariant_cast<Qt3DRender::RayCasting::QRay3D>(obj.property("ray")); |
| QCOMPARE(r.origin(), Vector3D(-1, -2, -3)); |
| QCOMPARE(r.direction(), Vector3D(-4, -5, -6).normalized()); |
| } |
| |
| void tst_QRay3D::metaTypes() |
| { |
| int id = qMetaTypeId<Qt3DRender::RayCasting::QRay3D>(); |
| QVERIFY(QMetaType::type("Qt3DRender::RayCasting::QRay3D") == id); |
| QCOMPARE(QByteArray(QMetaType::typeName(id)), QByteArray("Qt3DRender::RayCasting::QRay3D")); |
| QVERIFY(QMetaType::isRegistered(id)); |
| } |
| |
| void tst_QRay3D::shouldNotAllowNullDirection() |
| { |
| // GIVEN |
| Qt3DRender::RayCasting::QRay3D ray; |
| |
| QCOMPARE(ray.origin(), Vector3D(0, 0, 0)); |
| QCOMPARE(ray.direction(), Vector3D(0, 0, 1)); |
| |
| // WHEN |
| ray.setDirection(Vector3D(0, 0, 0)); |
| |
| // THEN |
| QCOMPARE(ray.direction(), Vector3D(0, 0, 1)); |
| } |
| |
| QTEST_APPLESS_MAIN(tst_QRay3D) |
| |
| #include "tst_qray3d.moc" |