1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Copyright (C) 2015 Klaralvdalens Datakonsult AB (KDAB).
5** Contact: https://www.qt.io/licensing/
6**
7** This file is part of the Qt3D module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:GPL-EXCEPT$
10** Commercial License Usage
11** Licensees holding valid commercial Qt licenses may use this file in
12** accordance with the commercial license agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and The Qt Company. For licensing terms
15** and conditions see https://www.qt.io/terms-conditions. For further
16** information use the contact form at https://www.qt.io/contact-us.
17**
18** GNU General Public License Usage
19** Alternatively, this file may be used under the terms of the GNU
20** General Public License version 3 as published by the Free Software
21** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
22** included in the packaging of this file. Please review the following
23** information to ensure the GNU General Public License requirements will
24** be met: https://www.gnu.org/licenses/gpl-3.0.html.
25**
26** $QT_END_LICENSE$
27**
28****************************************************************************/
29
30#include <QtTest/QtTest>
31#include <Qt3DRender/private/qray3d_p.h>
32
33class tst_QRay3D : public QObject
34{
35 Q_OBJECT
36public:
37 tst_QRay3D() {}
38 ~tst_QRay3D() {}
39
40private Q_SLOTS:
41 void create_data();
42 void create();
43 void projection_data();
44 void projection();
45 void point_data();
46 void point();
47 void contains_point_data();
48 void contains_point();
49 void contains_ray_data();
50 void contains_ray();
51 void distance_data();
52 void distance();
53 void compare();
54 void dataStream();
55 void transform_data();
56 void transform();
57 void properties();
58 void metaTypes();
59 void shouldNotAllowNullDirection();
60};
61
62// Fix the problem where a compared value happens to be zero (and
63// you cannot always predict this, and should not predict it
64// since then you produce self-fulling prophecies instead of tests).
65// In that case qFuzzyCompare has a completely strict criterion since
66// it finds the "fudge factor" by multiplying by zero...
67static inline bool fuzzyCompare(float p1, float p2)
68{
69 float fac = qMin(a: qAbs(t: p1), b: qAbs(t: p2));
70 return (qAbs(t: p1 - p2) <= (qIsNull(f: fac) ? 0.00001f : 0.00001f * fac));
71}
72
73static inline bool fuzzyCompare(const Vector3D &lhs, const Vector3D &rhs)
74{
75 if (fuzzyCompare(p1: lhs.x(), p2: rhs.x()) &&
76 fuzzyCompare(p1: lhs.y(), p2: rhs.y()) &&
77 fuzzyCompare(p1: lhs.z(), p2: rhs.z()))
78 return true;
79#ifndef QT_NO_DEBUG_STREAM
80 qWarning() << "actual:" << lhs;
81 qWarning() << "expected:" << rhs;
82#endif
83 return false;
84}
85
86void tst_QRay3D::create_data()
87{
88 QTest::addColumn<Vector3D>(name: "point");
89 QTest::addColumn<Vector3D>(name: "direction");
90
91 // normalized direction vectors
92 QTest::newRow(dataTag: "line on x-axis from origin")
93 << Vector3D()
94 << Vector3D(1.0f, 0.0f, 0.0f);
95
96 QTest::newRow(dataTag: "line parallel -z-axis from 3,3,3")
97 << Vector3D(3.0f, 3.0f, 3.0f)
98 << Vector3D(0.0f, 0.0f, -1.0f);
99
100 QTest::newRow(dataTag: "vertical line (parallel to y-axis)")
101 << Vector3D(0.5f, 0.0f, 0.5f)
102 << Vector3D(0.0f, 1.0f, 0.0f);
103
104 QTest::newRow(dataTag: "equidistant from all 3 axes")
105 << Vector3D(0.5f, 0.0f, 0.5f)
106 << Vector3D(0.57735026919f, 0.57735026919f, 0.57735026919f);
107
108 // non-normalized direction vectors
109 QTest::newRow(dataTag: "line on x-axis from origin - B")
110 << Vector3D()
111 << Vector3D(2.0f, 0.0f, 0.0f).normalized();
112
113 QTest::newRow(dataTag: "line parallel -z-axis from 3,3,3 - B")
114 << Vector3D(3.0f, 3.0f, 3.0f)
115 << Vector3D(0.0f, 0.0f, -0.7f).normalized();
116
117 QTest::newRow(dataTag: "vertical line (parallel to y-axis) - B")
118 << Vector3D(0.5f, 0.0f, 0.5f)
119 << Vector3D(0.0f, 5.3f, 0.0f).normalized();
120
121 QTest::newRow(dataTag: "equidistant from all 3 axes - B")
122 << Vector3D(0.5f, 0.0f, 0.5f)
123 << Vector3D(1.0f, 1.0f, 1.0f).normalized();
124
125 QTest::newRow(dataTag: "negative direction")
126 << Vector3D(-3.0f, -3.0f, -3.0f)
127 << Vector3D(-1.2f, -1.8f, -2.4f).normalized();
128}
129
130void tst_QRay3D::create()
131{
132 QFETCH(Vector3D, point);
133 QFETCH(Vector3D, direction);
134 Qt3DRender::RayCasting::QRay3D ray(point, direction);
135 QVERIFY(fuzzyCompare(ray.direction(), direction));
136 QVERIFY(fuzzyCompare(ray.origin(), point));
137
138 Qt3DRender::RayCasting::QRay3D ray2;
139 QCOMPARE(ray2.origin(), Vector3D(0, 0, 0));
140 QCOMPARE(ray2.direction(), Vector3D(0, 0, 1));
141 ray2.setOrigin(point);
142 ray2.setDirection(direction);
143 QVERIFY(fuzzyCompare(ray.direction(), direction));
144 QVERIFY(fuzzyCompare(ray.origin(), point));
145}
146
147void tst_QRay3D::projection_data()
148{
149 QTest::addColumn<Vector3D>(name: "point");
150 QTest::addColumn<Vector3D>(name: "direction");
151 QTest::addColumn<Vector3D>(name: "vector");
152 QTest::addColumn<Vector3D>(name: "expected");
153
154 QTest::newRow(dataTag: "line on x-axis from origin")
155 << Vector3D()
156 << Vector3D(2.0f, 0.0f, 0.0f)
157 << Vector3D(0.6f, 0.0f, 0.0f)
158 << Vector3D(0.6f, 0.0f, 0.0f);
159
160 QTest::newRow(dataTag: "line parallel -z-axis from 3,3,3")
161 << Vector3D(3.0f, 3.0f, 3.0f)
162 << Vector3D(0.0f, 0.0f, -0.7f)
163 << Vector3D(3.0f, 3.0f, 2.4f)
164 << Vector3D(0.0f, 0.0f, 2.4f);
165
166 QTest::newRow(dataTag: "vertical line (parallel to y-axis)")
167 << Vector3D(0.5f, 0.0f, 0.5f)
168 << Vector3D(0.0f, 5.3f, 0.0f)
169 << Vector3D(0.5f, 0.6f, 0.5f)
170 << Vector3D(0.0f, 0.6f, 0.0f);
171
172 QTest::newRow(dataTag: "equidistant from all 3 axes, project y-axis (with some z & x)")
173 << Vector3D(0.5f, 0.0f, 0.5f)
174 << Vector3D(1.0f, 1.0f, 1.0f)
175 << Vector3D(0.5f, 5.0f, 0.5f)
176 << Vector3D(2.0f, 2.0f, 2.0f);
177
178 QTest::newRow(dataTag: "negative direction line, project +ve y-axis (with some z & x)")
179 << Vector3D(-3.0f, -3.0f, -3.0f)
180 << Vector3D(-1.2f, -1.8f, -2.4f)
181 << Vector3D(0.5f, 5.0f, 0.5f)
182 << Vector3D(1.241379261016846f, 1.862068772315979f, 2.48275852203369f);
183}
184
185void tst_QRay3D::projection()
186{
187 QFETCH(Vector3D, point);
188 QFETCH(Vector3D, direction);
189 QFETCH(Vector3D, vector);
190 QFETCH(Vector3D, expected);
191 Qt3DRender::RayCasting::QRay3D line(point, direction);
192 Vector3D result = line.project(vector);
193 QVERIFY(fuzzyCompare(result, expected));
194}
195
196void tst_QRay3D::point_data()
197{
198 QTest::addColumn<Vector3D>(name: "point");
199 QTest::addColumn<Vector3D>(name: "direction");
200 QTest::addColumn<Vector3D>(name: "point_on_line_pos_0_6");
201 QTest::addColumn<Vector3D>(name: "point_on_line_neg_7_2");
202
203 QTest::newRow(dataTag: "line on x-axis from origin")
204 << Vector3D()
205 << Vector3D(2.0f, 0.0f, 0.0f)
206 << Vector3D(0.6f, 0.0f, 0.0f)
207 << Vector3D(-7.2f, 0.0f, 0.0f);
208
209 QTest::newRow(dataTag: "line parallel -z-axis from 3,3,3")
210 << Vector3D(3.0f, 3.0f, 3.0f)
211 << Vector3D(0.0f, 0.0f, -0.7f)
212 << Vector3D(3.0f, 3.0f, 2.4f)
213 << Vector3D(3.0f, 3.0f, 10.2f);
214
215 QTest::newRow(dataTag: "vertical line (parallel to y-axis)")
216 << Vector3D(0.5f, 0.0f, 0.5f)
217 << Vector3D(0.0f, 5.3f, 0.0f)
218 << Vector3D(0.5f, 0.6f, 0.5f)
219 << Vector3D(0.5f, -7.2f, 0.5f);
220
221 QTest::newRow(dataTag: "equidistant from all 3 axes")
222 << Vector3D(0.5f, 0.0f, 0.5f)
223 << Vector3D(1.0f, 1.0f, 1.0f)
224 << Vector3D(0.84641f, 0.34641f, 0.84641f)
225 << Vector3D(-3.65692f, -4.15692f, -3.65692f);
226
227 QTest::newRow(dataTag: "negative direction")
228 << Vector3D(-3.0f, -3.0f, -3.0f)
229 << Vector3D(-1.2f, -1.8f, -2.4f)
230 << Vector3D(-3.22283f, -3.33425f, -3.44567f)
231 << Vector3D(-0.325987f, 1.01102f, 2.34803f);
232}
233
234void tst_QRay3D::point()
235{
236 QFETCH(Vector3D, point);
237 QFETCH(Vector3D, direction);
238 QFETCH(Vector3D, point_on_line_pos_0_6);
239 QFETCH(Vector3D, point_on_line_neg_7_2);
240 Qt3DRender::RayCasting::QRay3D line(point, direction);
241 QVERIFY(fuzzyCompare(line.point(0.6f), point_on_line_pos_0_6));
242 QVERIFY(fuzzyCompare(line.point(-7.2f), point_on_line_neg_7_2));
243 QVERIFY(fuzzyCompare(line.projectedDistance(point_on_line_pos_0_6), 0.6f));
244 QVERIFY(fuzzyCompare(line.projectedDistance(point_on_line_neg_7_2), -7.2f));
245}
246
247void tst_QRay3D::contains_point_data()
248{
249 QTest::addColumn<Vector3D>(name: "origin");
250 QTest::addColumn<Vector3D>(name: "direction");
251 QTest::addColumn<Vector3D>(name: "point");
252 QTest::addColumn<bool>(name: "contains");
253
254 QTest::newRow(dataTag: "bogus this line with null direction")
255 << Vector3D(1.0, 3.0, 3.0)
256 << Vector3D(0.0, 0.0, 0.0)
257 << Vector3D(1.0, 2.0, 4.0)
258 << false;
259
260 QTest::newRow(dataTag: "point at the origin")
261 << Vector3D(0.0, 0.0, 0.0)
262 << Vector3D(1.0, 3.0, 3.0)
263 << Vector3D(0.0, 0.0, 0.0)
264 << true;
265
266 QTest::newRow(dataTag: "close to the origin")
267 << Vector3D(1.0, 1.0, 1.0)
268 << Vector3D(1.0, 3.0, 3.0)
269 << Vector3D(1.0005f, 1.0005f, 1.0)
270 << false;
271
272 QTest::newRow(dataTag: "45 line line in plane x=1")
273 << Vector3D(1.0, 3.0, 3.0)
274 << Vector3D(0.0, -1.0, -1.0)
275 << Vector3D(1.0, 4.0, 4.0)
276 << true;
277 {
278 // This is to prove that the constructed approach give the
279 // same results
280 Vector3D p(1.0, 3.0, 3.0);
281 Vector3D v(0.0, -1.0, -1.0);
282
283 QTest::newRow(dataTag: "constructed 45 line line in plane x=1")
284 << p
285 << v
286 << p + v
287 << true;
288 }
289
290 QTest::newRow(dataTag: "intersection with negative s in plane z=-1")
291 << Vector3D(1.0f, 2.0f, -1.0f)
292 << Vector3D(1.0f, 1.0f, 0.0f)
293 << Vector3D(2.0f, 1.0f, 0.0f)
294 << false;
295
296 QTest::newRow(dataTag: "45 angled line")
297 << Vector3D(3.0f, 0.0f, -1.0f)
298 << Vector3D(1.0f, -1.0f, 1.0f)
299 << Vector3D(6.0f, -3.0f, 2.0f)
300 << true;
301
302 {
303 Vector3D p(-10.0, 3.0, 3.0);
304 Vector3D v(0.0, 20.0, -1.0);
305 QTest::newRow(dataTag: "constructed vector close to axis")
306 << p
307 << v
308 << p + v * 3.0
309 << true;
310 }
311
312 {
313 Vector3D p(1.0, 3.0, 3.0);
314 Vector3D v(40.0, 500.0, -1.0);
315 QTest::newRow(dataTag: "constructed larger values close to axis")
316 << p
317 << v
318 << p + v
319 << true;
320 }
321}
322
323void tst_QRay3D::contains_point()
324{
325 QFETCH(Vector3D, origin);
326 QFETCH(Vector3D, direction);
327 QFETCH(Vector3D, point);
328 QFETCH(bool, contains);
329
330 Qt3DRender::RayCasting::QRay3D line(origin, direction);
331 QCOMPARE(line.contains(point), contains);
332}
333
334void tst_QRay3D::contains_ray_data()
335{
336 contains_point_data();
337}
338
339void tst_QRay3D::contains_ray()
340{
341 QFETCH(Vector3D, origin);
342 QFETCH(Vector3D, direction);
343 QFETCH(Vector3D, point);
344 QFETCH(bool, contains);
345
346 Qt3DRender::RayCasting::QRay3D line(origin, direction);
347 if (contains) {
348 Qt3DRender::RayCasting::QRay3D line2(point, direction);
349 QVERIFY(line.contains(line2));
350 QVERIFY(line2.contains(line));
351
352 // Reversed direction is also contained.
353 Qt3DRender::RayCasting::QRay3D line3(point, -direction);
354 QVERIFY(line.contains(line2));
355 QVERIFY(line2.contains(line));
356
357 // Different direction.
358 Qt3DRender::RayCasting::QRay3D line4(point, Vector3D(direction.y(), direction.x(), direction.z()));
359 QVERIFY(!line.contains(line4));
360 QVERIFY(!line4.contains(line));
361 } else {
362 Qt3DRender::RayCasting::QRay3D line2(point, direction);
363 QVERIFY(!line.contains(line2));
364 QVERIFY(!line2.contains(line));
365 }
366}
367
368void tst_QRay3D::distance_data()
369{
370 QTest::addColumn<Vector3D>(name: "origin");
371 QTest::addColumn<Vector3D>(name: "direction");
372 QTest::addColumn<Vector3D>(name: "point");
373 QTest::addColumn<float>(name: "distance");
374
375 QTest::newRow(dataTag: "axis-x")
376 << Vector3D(6.0f, 0.0f, 0.0f)
377 << Vector3D(1.0f, 0.0f, 0.0f)
378 << Vector3D(0.0f, 0.0f, 0.0f)
379 << 0.0f;
380
381 QTest::newRow(dataTag: "axis-x to 1")
382 << Vector3D(6.0f, 0.0f, 0.0f)
383 << Vector3D(1.0f, 0.0f, 0.0f)
384 << Vector3D(0.0f, 1.0f, 0.0f)
385 << 1.0f;
386
387 QTest::newRow(dataTag: "neg-axis-y")
388 << Vector3D(0.0f, 6.0f, 0.0f)
389 << Vector3D(0.0f, -1.5f, 0.0f)
390 << Vector3D(0.0f, 100.0f, 0.0f)
391 << 0.0f;
392
393 QTest::newRow(dataTag: "neg-axis-y to 2")
394 << Vector3D(0.0f, 6.0f, 0.0f)
395 << Vector3D(0.0f, -1.5f, 0.0f)
396 << Vector3D(2.0f, 0.0f, 0.0f)
397 << 2.0f;
398}
399
400void tst_QRay3D::distance()
401{
402 QFETCH(Vector3D, origin);
403 QFETCH(Vector3D, direction);
404 QFETCH(Vector3D, point);
405 QFETCH(float, distance);
406
407 Qt3DRender::RayCasting::QRay3D line(origin, direction);
408 QCOMPARE(line.distance(point), distance);
409}
410
411void tst_QRay3D::compare()
412{
413 Qt3DRender::RayCasting::QRay3D ray1(Vector3D(10, 20, 30), Vector3D(-3, -4, -5));
414 Qt3DRender::RayCasting::QRay3D ray2(Vector3D(10, 20, 30), Vector3D(1.5f, 2.0f, 2.5f));
415 Qt3DRender::RayCasting::QRay3D ray3(Vector3D(0, 20, 30), Vector3D(-3, -4, -5));
416 QVERIFY(ray1 == ray1);
417 QVERIFY(!(ray1 != ray1));
418 QVERIFY(qFuzzyCompare(ray1, ray1));
419 QVERIFY(ray1 != ray2);
420 QVERIFY(!(ray1 == ray2));
421 QVERIFY(!qFuzzyCompare(ray1, ray2));
422 QVERIFY(ray1 != ray3);
423 QVERIFY(!(ray1 == ray3));
424 QVERIFY(!qFuzzyCompare(ray1, ray3));
425}
426
427void tst_QRay3D::dataStream()
428{
429#ifndef QT_NO_DATASTREAM
430 Qt3DRender::RayCasting::QRay3D ray(Vector3D(1.0f, 2.0f, 3.0f), Vector3D(4.0f, 5.0f, 6.0f));
431
432 QByteArray data;
433 {
434 QDataStream stream(&data, QIODevice::WriteOnly);
435 stream << ray;
436 }
437
438 Qt3DRender::RayCasting::QRay3D ray2;
439 {
440 QDataStream stream2(data);
441 stream2 >> ray2;
442 }
443
444 QVERIFY(ray == ray2);
445#endif
446}
447
448void tst_QRay3D::transform_data()
449{
450 create_data();
451}
452
453void tst_QRay3D::transform()
454{
455 QFETCH(Vector3D, point);
456 QFETCH(Vector3D, direction);
457
458 Matrix4x4 m;
459 {
460 QMatrix4x4 c;
461 c.translate(x: -1.0f, y: 2.5f, z: 5.0f);
462 c.rotate(angle: 45.0f, x: 1.0f, y: 1.0f, z: 1.0f);
463 c.scale(factor: 23.5f);
464 m = Matrix4x4(c);
465 }
466
467 Qt3DRender::RayCasting::QRay3D ray1(point, direction);
468 Qt3DRender::RayCasting::QRay3D ray2(ray1);
469 Qt3DRender::RayCasting::QRay3D ray3;
470
471 ray1.transform(matrix: m);
472 ray3 = ray2.transformed(matrix: m);
473
474 QVERIFY(fuzzyCompare(ray1.origin(), ray3.origin()));
475 QVERIFY(fuzzyCompare(ray1.direction(), ray3.direction()));
476
477 QVERIFY(fuzzyCompare(ray1.origin(), m * point));
478 QVERIFY(fuzzyCompare(ray1.direction(), m.mapVector(direction).normalized()));
479}
480
481class tst_QRay3DProperties : public QObject
482{
483 Q_OBJECT
484 Q_PROPERTY(Qt3DRender::RayCasting::QRay3D ray READ ray WRITE setRay)
485public:
486 tst_QRay3DProperties(QObject *parent = 0) : QObject(parent) {}
487
488 Qt3DRender::RayCasting::QRay3D ray() const { return r; }
489 void setRay(const Qt3DRender::RayCasting::QRay3D& value) { r = value; }
490
491private:
492 Qt3DRender::RayCasting::QRay3D r;
493};
494
495// Test getting and setting properties via the metaobject system.
496void tst_QRay3D::properties()
497{
498 tst_QRay3DProperties obj;
499
500 qRegisterMetaType<Qt3DRender::RayCasting::QRay3D>();
501
502 obj.setRay(Qt3DRender::RayCasting::QRay3D(Vector3D(1, 2, 3), Vector3D(4, 5, 6)));
503
504 Qt3DRender::RayCasting::QRay3D r = qvariant_cast<Qt3DRender::RayCasting::QRay3D>(v: obj.property(name: "ray"));
505 QCOMPARE(r.origin(), Vector3D(1, 2, 3));
506 QCOMPARE(r.direction(), Vector3D(4, 5, 6).normalized());
507
508 obj.setProperty(name: "ray",
509 value: QVariant::fromValue
510 (value: Qt3DRender::RayCasting::QRay3D(Vector3D(-1, -2, -3), Vector3D(-4, -5, -6))));
511
512 r = qvariant_cast<Qt3DRender::RayCasting::QRay3D>(v: obj.property(name: "ray"));
513 QCOMPARE(r.origin(), Vector3D(-1, -2, -3));
514 QCOMPARE(r.direction(), Vector3D(-4, -5, -6).normalized());
515}
516
517void tst_QRay3D::metaTypes()
518{
519 int id = qMetaTypeId<Qt3DRender::RayCasting::QRay3D>();
520 QVERIFY(QMetaType::type("Qt3DRender::RayCasting::QRay3D") == id);
521 QCOMPARE(QByteArray(QMetaType::typeName(id)), QByteArray("Qt3DRender::RayCasting::QRay3D"));
522 QVERIFY(QMetaType::isRegistered(id));
523}
524
525void tst_QRay3D::shouldNotAllowNullDirection()
526{
527 // GIVEN
528 Qt3DRender::RayCasting::QRay3D ray;
529
530 QCOMPARE(ray.origin(), Vector3D(0, 0, 0));
531 QCOMPARE(ray.direction(), Vector3D(0, 0, 1));
532
533 // WHEN
534 ray.setDirection(Vector3D(0, 0, 0));
535
536 // THEN
537 QCOMPARE(ray.direction(), Vector3D(0, 0, 1));
538}
539
540QTEST_APPLESS_MAIN(tst_QRay3D)
541
542#include "tst_qray3d.moc"
543

source code of qt3d/tests/auto/render/qray3d/tst_qray3d.cpp