Revision control
++ // Copyright (C) 2016 The Qt Company Ltd.
++ // Copyright (C) 2015 Klaralvdalens Datakonsult AB (KDAB).
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
++
++ #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::fromName("Qt3DRender::RayCasting::QRay3D").id() == id);
++ QCOMPARE(QByteArray(QMetaType(id).name()), 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"