Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/tests/core/math/test_quaternion.h
10278 views
1
/**************************************************************************/
2
/* test_quaternion.h */
3
/**************************************************************************/
4
/* This file is part of: */
5
/* GODOT ENGINE */
6
/* https://godotengine.org */
7
/**************************************************************************/
8
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10
/* */
11
/* Permission is hereby granted, free of charge, to any person obtaining */
12
/* a copy of this software and associated documentation files (the */
13
/* "Software"), to deal in the Software without restriction, including */
14
/* without limitation the rights to use, copy, modify, merge, publish, */
15
/* distribute, sublicense, and/or sell copies of the Software, and to */
16
/* permit persons to whom the Software is furnished to do so, subject to */
17
/* the following conditions: */
18
/* */
19
/* The above copyright notice and this permission notice shall be */
20
/* included in all copies or substantial portions of the Software. */
21
/* */
22
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29
/**************************************************************************/
30
31
#pragma once
32
33
#include "core/math/math_defs.h"
34
#include "core/math/math_funcs.h"
35
#include "core/math/quaternion.h"
36
#include "core/math/vector3.h"
37
38
#include "tests/test_macros.h"
39
40
namespace TestQuaternion {
41
42
Quaternion quat_euler_yxz_deg(Vector3 angle) {
43
double yaw = Math::deg_to_rad(angle[1]);
44
double pitch = Math::deg_to_rad(angle[0]);
45
double roll = Math::deg_to_rad(angle[2]);
46
47
// Generate YXZ (Z-then-X-then-Y) Quaternion using single-axis Euler
48
// constructor and quaternion product, both tested separately.
49
Quaternion q_y = Quaternion::from_euler(Vector3(0.0, yaw, 0.0));
50
Quaternion q_p = Quaternion::from_euler(Vector3(pitch, 0.0, 0.0));
51
Quaternion q_r = Quaternion::from_euler(Vector3(0.0, 0.0, roll));
52
// Roll-Z is followed by Pitch-X, then Yaw-Y.
53
Quaternion q_yxz = q_y * q_p * q_r;
54
55
return q_yxz;
56
}
57
58
TEST_CASE("[Quaternion] Default Construct") {
59
constexpr Quaternion q;
60
61
CHECK(q[0] == 0.0);
62
CHECK(q[1] == 0.0);
63
CHECK(q[2] == 0.0);
64
CHECK(q[3] == 1.0);
65
}
66
67
TEST_CASE("[Quaternion] Construct x,y,z,w") {
68
// Values are taken from actual use in another project & are valid (except roundoff error).
69
constexpr Quaternion q(0.2391, 0.099, 0.3696, 0.8924);
70
71
CHECK(q[0] == doctest::Approx(0.2391));
72
CHECK(q[1] == doctest::Approx(0.099));
73
CHECK(q[2] == doctest::Approx(0.3696));
74
CHECK(q[3] == doctest::Approx(0.8924));
75
}
76
77
TEST_CASE("[Quaternion] Construct AxisAngle 1") {
78
// Easy to visualize: 120 deg about X-axis.
79
Quaternion q(Vector3(1.0, 0.0, 0.0), Math::deg_to_rad(120.0));
80
81
// 0.866 isn't close enough; doctest::Approx doesn't cut much slack!
82
CHECK(q[0] == doctest::Approx(0.866025)); // Sine of half the angle.
83
CHECK(q[1] == doctest::Approx(0.0));
84
CHECK(q[2] == doctest::Approx(0.0));
85
CHECK(q[3] == doctest::Approx(0.5)); // Cosine of half the angle.
86
}
87
88
TEST_CASE("[Quaternion] Construct AxisAngle 2") {
89
// Easy to visualize: 30 deg about Y-axis.
90
Quaternion q(Vector3(0.0, 1.0, 0.0), Math::deg_to_rad(30.0));
91
92
CHECK(q[0] == doctest::Approx(0.0));
93
CHECK(q[1] == doctest::Approx(0.258819)); // Sine of half the angle.
94
CHECK(q[2] == doctest::Approx(0.0));
95
CHECK(q[3] == doctest::Approx(0.965926)); // Cosine of half the angle.
96
}
97
98
TEST_CASE("[Quaternion] Construct AxisAngle 3") {
99
// Easy to visualize: 60 deg about Z-axis.
100
Quaternion q(Vector3(0.0, 0.0, 1.0), Math::deg_to_rad(60.0));
101
102
CHECK(q[0] == doctest::Approx(0.0));
103
CHECK(q[1] == doctest::Approx(0.0));
104
CHECK(q[2] == doctest::Approx(0.5)); // Sine of half the angle.
105
CHECK(q[3] == doctest::Approx(0.866025)); // Cosine of half the angle.
106
}
107
108
TEST_CASE("[Quaternion] Construct AxisAngle 4") {
109
// More complex & hard to visualize, so test w/ data from online calculator.
110
constexpr Vector3 axis(1.0, 2.0, 0.5);
111
Quaternion q(axis.normalized(), Math::deg_to_rad(35.0));
112
113
CHECK(q[0] == doctest::Approx(0.131239));
114
CHECK(q[1] == doctest::Approx(0.262478));
115
CHECK(q[2] == doctest::Approx(0.0656194));
116
CHECK(q[3] == doctest::Approx(0.953717));
117
}
118
119
TEST_CASE("[Quaternion] Construct from Quaternion") {
120
constexpr Vector3 axis(1.0, 2.0, 0.5);
121
Quaternion q_src(axis.normalized(), Math::deg_to_rad(35.0));
122
Quaternion q(q_src);
123
124
CHECK(q[0] == doctest::Approx(0.131239));
125
CHECK(q[1] == doctest::Approx(0.262478));
126
CHECK(q[2] == doctest::Approx(0.0656194));
127
CHECK(q[3] == doctest::Approx(0.953717));
128
}
129
130
TEST_CASE("[Quaternion] Construct Euler SingleAxis") {
131
double yaw = Math::deg_to_rad(45.0);
132
double pitch = Math::deg_to_rad(30.0);
133
double roll = Math::deg_to_rad(10.0);
134
135
Vector3 euler_y(0.0, yaw, 0.0);
136
Quaternion q_y = Quaternion::from_euler(euler_y);
137
CHECK(q_y[0] == doctest::Approx(0.0));
138
CHECK(q_y[1] == doctest::Approx(0.382684));
139
CHECK(q_y[2] == doctest::Approx(0.0));
140
CHECK(q_y[3] == doctest::Approx(0.923879));
141
142
Vector3 euler_p(pitch, 0.0, 0.0);
143
Quaternion q_p = Quaternion::from_euler(euler_p);
144
CHECK(q_p[0] == doctest::Approx(0.258819));
145
CHECK(q_p[1] == doctest::Approx(0.0));
146
CHECK(q_p[2] == doctest::Approx(0.0));
147
CHECK(q_p[3] == doctest::Approx(0.965926));
148
149
Vector3 euler_r(0.0, 0.0, roll);
150
Quaternion q_r = Quaternion::from_euler(euler_r);
151
CHECK(q_r[0] == doctest::Approx(0.0));
152
CHECK(q_r[1] == doctest::Approx(0.0));
153
CHECK(q_r[2] == doctest::Approx(0.0871558));
154
CHECK(q_r[3] == doctest::Approx(0.996195));
155
}
156
157
TEST_CASE("[Quaternion] Construct Euler YXZ dynamic axes") {
158
double yaw = Math::deg_to_rad(45.0);
159
double pitch = Math::deg_to_rad(30.0);
160
double roll = Math::deg_to_rad(10.0);
161
162
// Generate YXZ comparison data (Z-then-X-then-Y) using single-axis Euler
163
// constructor and quaternion product, both tested separately.
164
Vector3 euler_y(0.0, yaw, 0.0);
165
Quaternion q_y = Quaternion::from_euler(euler_y);
166
Vector3 euler_p(pitch, 0.0, 0.0);
167
Quaternion q_p = Quaternion::from_euler(euler_p);
168
Vector3 euler_r(0.0, 0.0, roll);
169
Quaternion q_r = Quaternion::from_euler(euler_r);
170
171
// Instrinsically, Yaw-Y then Pitch-X then Roll-Z.
172
// Extrinsically, Roll-Z is followed by Pitch-X, then Yaw-Y.
173
Quaternion check_yxz = q_y * q_p * q_r;
174
175
// Test construction from YXZ Euler angles.
176
Vector3 euler_yxz(pitch, yaw, roll);
177
Quaternion q = Quaternion::from_euler(euler_yxz);
178
CHECK(q[0] == doctest::Approx(check_yxz[0]));
179
CHECK(q[1] == doctest::Approx(check_yxz[1]));
180
CHECK(q[2] == doctest::Approx(check_yxz[2]));
181
CHECK(q[3] == doctest::Approx(check_yxz[3]));
182
183
CHECK(q.is_equal_approx(check_yxz));
184
CHECK(q.get_euler().is_equal_approx(euler_yxz));
185
CHECK(check_yxz.get_euler().is_equal_approx(euler_yxz));
186
}
187
188
TEST_CASE("[Quaternion] Construct Basis Euler") {
189
double yaw = Math::deg_to_rad(45.0);
190
double pitch = Math::deg_to_rad(30.0);
191
double roll = Math::deg_to_rad(10.0);
192
Vector3 euler_yxz(pitch, yaw, roll);
193
Quaternion q_yxz = Quaternion::from_euler(euler_yxz);
194
Basis basis_axes = Basis::from_euler(euler_yxz);
195
Quaternion q(basis_axes);
196
CHECK(q.is_equal_approx(q_yxz));
197
}
198
199
TEST_CASE("[Quaternion] Construct Basis Axes") {
200
// Arbitrary Euler angles.
201
const Vector3 euler_yxz(Math::deg_to_rad(31.41), Math::deg_to_rad(-49.16), Math::deg_to_rad(12.34));
202
// Basis vectors from online calculation of rotation matrix.
203
constexpr Vector3 i_unit(0.5545787, 0.1823950, 0.8118957);
204
constexpr Vector3 j_unit(-0.5249245, 0.8337420, 0.1712555);
205
constexpr Vector3 k_unit(-0.6456754, -0.5211586, 0.5581192);
206
// Quaternion from online calculation.
207
constexpr Quaternion q_calc(0.2016913, -0.4245716, 0.206033, 0.8582598);
208
// Quaternion from local calculation.
209
const Quaternion q_local = quat_euler_yxz_deg(Vector3(31.41, -49.16, 12.34));
210
// Quaternion from Euler angles constructor.
211
const Quaternion q_euler = Quaternion::from_euler(euler_yxz);
212
CHECK(q_calc.is_equal_approx(q_local));
213
CHECK(q_local.is_equal_approx(q_euler));
214
215
// Calculate Basis and construct Quaternion.
216
// When this is written, C++ Basis class does not construct from basis vectors.
217
// This is by design, but may be subject to change.
218
// Workaround by constructing Basis from Euler angles.
219
// basis_axes = Basis(i_unit, j_unit, k_unit);
220
Basis basis_axes = Basis::from_euler(euler_yxz);
221
Quaternion q(basis_axes);
222
223
CHECK(basis_axes.get_column(0).is_equal_approx(i_unit));
224
CHECK(basis_axes.get_column(1).is_equal_approx(j_unit));
225
CHECK(basis_axes.get_column(2).is_equal_approx(k_unit));
226
227
CHECK(q.is_equal_approx(q_calc));
228
CHECK_FALSE(q.inverse().is_equal_approx(q_calc));
229
CHECK(q.is_equal_approx(q_local));
230
CHECK(q.is_equal_approx(q_euler));
231
CHECK(q[0] == doctest::Approx(0.2016913));
232
CHECK(q[1] == doctest::Approx(-0.4245716));
233
CHECK(q[2] == doctest::Approx(0.206033));
234
CHECK(q[3] == doctest::Approx(0.8582598));
235
}
236
237
TEST_CASE("[Quaternion] Construct Shortest Arc For 180 Degree Arc") {
238
Vector3 up(0, 1, 0);
239
Vector3 down(0, -1, 0);
240
Vector3 left(-1, 0, 0);
241
Vector3 right(1, 0, 0);
242
Vector3 forward(0, 0, -1);
243
Vector3 back(0, 0, 1);
244
245
// When we have a 180 degree rotation quaternion which was defined as
246
// A to B, logically when we transform A we expect to get B.
247
Quaternion left_to_right(left, right);
248
Quaternion right_to_left(right, left);
249
CHECK(left_to_right.xform(left).is_equal_approx(right));
250
CHECK(Quaternion(right, left).xform(right).is_equal_approx(left));
251
CHECK(Quaternion(up, down).xform(up).is_equal_approx(down));
252
CHECK(Quaternion(down, up).xform(down).is_equal_approx(up));
253
CHECK(Quaternion(forward, back).xform(forward).is_equal_approx(back));
254
CHECK(Quaternion(back, forward).xform(back).is_equal_approx(forward));
255
256
// With (arbitrary) opposite vectors that are not axis-aligned as parameters.
257
Vector3 diagonal_up = Vector3(1.2, 2.3, 4.5).normalized();
258
Vector3 diagonal_down = -diagonal_up;
259
Quaternion q1(diagonal_up, diagonal_down);
260
CHECK(q1.xform(diagonal_down).is_equal_approx(diagonal_up));
261
CHECK(q1.xform(diagonal_up).is_equal_approx(diagonal_down));
262
263
// For the consistency of the rotation direction, they should be symmetrical to the plane.
264
CHECK(left_to_right.is_equal_approx(right_to_left.inverse()));
265
266
// If vectors are same, no rotation.
267
CHECK(Quaternion(diagonal_up, diagonal_up).is_equal_approx(Quaternion()));
268
}
269
270
TEST_CASE("[Quaternion] Get Euler Orders") {
271
double x = Math::deg_to_rad(30.0);
272
double y = Math::deg_to_rad(45.0);
273
double z = Math::deg_to_rad(10.0);
274
Vector3 euler(x, y, z);
275
for (int i = 0; i < 6; i++) {
276
EulerOrder order = (EulerOrder)i;
277
Basis basis = Basis::from_euler(euler, order);
278
Quaternion q = Quaternion(basis);
279
Vector3 check = q.get_euler(order);
280
CHECK_MESSAGE(check.is_equal_approx(euler),
281
"Quaternion get_euler method should return the original angles.");
282
CHECK_MESSAGE(check.is_equal_approx(basis.get_euler(order)),
283
"Quaternion get_euler method should behave the same as Basis get_euler.");
284
}
285
}
286
287
TEST_CASE("[Quaternion] Product (book)") {
288
// Example from "Quaternions and Rotation Sequences" by Jack Kuipers, p. 108.
289
constexpr Quaternion p(1.0, -2.0, 1.0, 3.0);
290
constexpr Quaternion q(-1.0, 2.0, 3.0, 2.0);
291
292
constexpr Quaternion pq = p * q;
293
CHECK(pq[0] == doctest::Approx(-9.0));
294
CHECK(pq[1] == doctest::Approx(-2.0));
295
CHECK(pq[2] == doctest::Approx(11.0));
296
CHECK(pq[3] == doctest::Approx(8.0));
297
}
298
299
TEST_CASE("[Quaternion] Product") {
300
double yaw = Math::deg_to_rad(45.0);
301
double pitch = Math::deg_to_rad(30.0);
302
double roll = Math::deg_to_rad(10.0);
303
304
Vector3 euler_y(0.0, yaw, 0.0);
305
Quaternion q_y = Quaternion::from_euler(euler_y);
306
CHECK(q_y[0] == doctest::Approx(0.0));
307
CHECK(q_y[1] == doctest::Approx(0.382684));
308
CHECK(q_y[2] == doctest::Approx(0.0));
309
CHECK(q_y[3] == doctest::Approx(0.923879));
310
311
Vector3 euler_p(pitch, 0.0, 0.0);
312
Quaternion q_p = Quaternion::from_euler(euler_p);
313
CHECK(q_p[0] == doctest::Approx(0.258819));
314
CHECK(q_p[1] == doctest::Approx(0.0));
315
CHECK(q_p[2] == doctest::Approx(0.0));
316
CHECK(q_p[3] == doctest::Approx(0.965926));
317
318
Vector3 euler_r(0.0, 0.0, roll);
319
Quaternion q_r = Quaternion::from_euler(euler_r);
320
CHECK(q_r[0] == doctest::Approx(0.0));
321
CHECK(q_r[1] == doctest::Approx(0.0));
322
CHECK(q_r[2] == doctest::Approx(0.0871558));
323
CHECK(q_r[3] == doctest::Approx(0.996195));
324
325
// Test ZYX dynamic-axes since test data is available online.
326
// Rotate first about X axis, then new Y axis, then new Z axis.
327
// (Godot uses YXZ Yaw-Pitch-Roll order).
328
Quaternion q_yp = q_y * q_p;
329
CHECK(q_yp[0] == doctest::Approx(0.239118));
330
CHECK(q_yp[1] == doctest::Approx(0.369644));
331
CHECK(q_yp[2] == doctest::Approx(-0.099046));
332
CHECK(q_yp[3] == doctest::Approx(0.892399));
333
334
Quaternion q_ryp = q_r * q_yp;
335
CHECK(q_ryp[0] == doctest::Approx(0.205991));
336
CHECK(q_ryp[1] == doctest::Approx(0.389078));
337
CHECK(q_ryp[2] == doctest::Approx(-0.0208912));
338
CHECK(q_ryp[3] == doctest::Approx(0.897636));
339
}
340
341
TEST_CASE("[Quaternion] xform unit vectors") {
342
// Easy to visualize: 120 deg about X-axis.
343
// Transform the i, j, & k unit vectors.
344
Quaternion q(Vector3(1.0, 0.0, 0.0), Math::deg_to_rad(120.0));
345
Vector3 i_t = q.xform(Vector3(1.0, 0.0, 0.0));
346
Vector3 j_t = q.xform(Vector3(0.0, 1.0, 0.0));
347
Vector3 k_t = q.xform(Vector3(0.0, 0.0, 1.0));
348
//
349
CHECK(i_t.is_equal_approx(Vector3(1.0, 0.0, 0.0)));
350
CHECK(j_t.is_equal_approx(Vector3(0.0, -0.5, 0.866025)));
351
CHECK(k_t.is_equal_approx(Vector3(0.0, -0.866025, -0.5)));
352
CHECK(i_t.length_squared() == doctest::Approx(1.0));
353
CHECK(j_t.length_squared() == doctest::Approx(1.0));
354
CHECK(k_t.length_squared() == doctest::Approx(1.0));
355
356
// Easy to visualize: 30 deg about Y-axis.
357
q = Quaternion(Vector3(0.0, 1.0, 0.0), Math::deg_to_rad(30.0));
358
i_t = q.xform(Vector3(1.0, 0.0, 0.0));
359
j_t = q.xform(Vector3(0.0, 1.0, 0.0));
360
k_t = q.xform(Vector3(0.0, 0.0, 1.0));
361
//
362
CHECK(i_t.is_equal_approx(Vector3(0.866025, 0.0, -0.5)));
363
CHECK(j_t.is_equal_approx(Vector3(0.0, 1.0, 0.0)));
364
CHECK(k_t.is_equal_approx(Vector3(0.5, 0.0, 0.866025)));
365
CHECK(i_t.length_squared() == doctest::Approx(1.0));
366
CHECK(j_t.length_squared() == doctest::Approx(1.0));
367
CHECK(k_t.length_squared() == doctest::Approx(1.0));
368
369
// Easy to visualize: 60 deg about Z-axis.
370
q = Quaternion(Vector3(0.0, 0.0, 1.0), Math::deg_to_rad(60.0));
371
i_t = q.xform(Vector3(1.0, 0.0, 0.0));
372
j_t = q.xform(Vector3(0.0, 1.0, 0.0));
373
k_t = q.xform(Vector3(0.0, 0.0, 1.0));
374
//
375
CHECK(i_t.is_equal_approx(Vector3(0.5, 0.866025, 0.0)));
376
CHECK(j_t.is_equal_approx(Vector3(-0.866025, 0.5, 0.0)));
377
CHECK(k_t.is_equal_approx(Vector3(0.0, 0.0, 1.0)));
378
CHECK(i_t.length_squared() == doctest::Approx(1.0));
379
CHECK(j_t.length_squared() == doctest::Approx(1.0));
380
CHECK(k_t.length_squared() == doctest::Approx(1.0));
381
}
382
383
TEST_CASE("[Quaternion] xform vector") {
384
// Arbitrary quaternion rotates an arbitrary vector.
385
const Vector3 euler_yzx(Math::deg_to_rad(31.41), Math::deg_to_rad(-49.16), Math::deg_to_rad(12.34));
386
const Basis basis_axes = Basis::from_euler(euler_yzx);
387
const Quaternion q(basis_axes);
388
389
constexpr Vector3 v_arb(3.0, 4.0, 5.0);
390
const Vector3 v_rot = q.xform(v_arb);
391
const Vector3 v_compare = basis_axes.xform(v_arb);
392
393
CHECK(v_rot.length_squared() == doctest::Approx(v_arb.length_squared()));
394
CHECK(v_rot.is_equal_approx(v_compare));
395
}
396
397
// Test vector xform for a single combination of Quaternion and Vector.
398
void test_quat_vec_rotate(Vector3 euler_yzx, Vector3 v_in) {
399
const Basis basis_axes = Basis::from_euler(euler_yzx);
400
const Quaternion q(basis_axes);
401
402
const Vector3 v_rot = q.xform(v_in);
403
const Vector3 v_compare = basis_axes.xform(v_in);
404
405
CHECK(v_rot.length_squared() == doctest::Approx(v_in.length_squared()));
406
CHECK(v_rot.is_equal_approx(v_compare));
407
}
408
409
TEST_CASE("[Stress][Quaternion] Many vector xforms") {
410
// Many arbitrary quaternions rotate many arbitrary vectors.
411
// For each trial, check that rotation by Quaternion yields same result as
412
// rotation by Basis.
413
constexpr int STEPS = 100; // Number of test steps in each dimension
414
constexpr double delta = 2.0 * Math::PI / STEPS; // Angle increment per step
415
constexpr double delta_vec = 20.0 / STEPS; // Vector increment per step
416
Vector3 vec_arb(1.0, 1.0, 1.0);
417
double x_angle = -Math::PI;
418
double y_angle = -Math::PI;
419
double z_angle = -Math::PI;
420
for (double i = 0; i < STEPS; ++i) {
421
vec_arb[0] = -10.0 + i * delta_vec;
422
x_angle = i * delta - Math::PI;
423
for (double j = 0; j < STEPS; ++j) {
424
vec_arb[1] = -10.0 + j * delta_vec;
425
y_angle = j * delta - Math::PI;
426
for (double k = 0; k < STEPS; ++k) {
427
vec_arb[2] = -10.0 + k * delta_vec;
428
z_angle = k * delta - Math::PI;
429
Vector3 euler_yzx(x_angle, y_angle, z_angle);
430
test_quat_vec_rotate(euler_yzx, vec_arb);
431
}
432
}
433
}
434
}
435
436
TEST_CASE("[Quaternion] Finite number checks") {
437
constexpr real_t x = Math::NaN;
438
439
CHECK_MESSAGE(
440
Quaternion(0, 1, 2, 3).is_finite(),
441
"Quaternion with all components finite should be finite");
442
443
CHECK_FALSE_MESSAGE(
444
Quaternion(x, 1, 2, 3).is_finite(),
445
"Quaternion with one component infinite should not be finite.");
446
CHECK_FALSE_MESSAGE(
447
Quaternion(0, x, 2, 3).is_finite(),
448
"Quaternion with one component infinite should not be finite.");
449
CHECK_FALSE_MESSAGE(
450
Quaternion(0, 1, x, 3).is_finite(),
451
"Quaternion with one component infinite should not be finite.");
452
CHECK_FALSE_MESSAGE(
453
Quaternion(0, 1, 2, x).is_finite(),
454
"Quaternion with one component infinite should not be finite.");
455
456
CHECK_FALSE_MESSAGE(
457
Quaternion(x, x, 2, 3).is_finite(),
458
"Quaternion with two components infinite should not be finite.");
459
CHECK_FALSE_MESSAGE(
460
Quaternion(x, 1, x, 3).is_finite(),
461
"Quaternion with two components infinite should not be finite.");
462
CHECK_FALSE_MESSAGE(
463
Quaternion(x, 1, 2, x).is_finite(),
464
"Quaternion with two components infinite should not be finite.");
465
CHECK_FALSE_MESSAGE(
466
Quaternion(0, x, x, 3).is_finite(),
467
"Quaternion with two components infinite should not be finite.");
468
CHECK_FALSE_MESSAGE(
469
Quaternion(0, x, 2, x).is_finite(),
470
"Quaternion with two components infinite should not be finite.");
471
CHECK_FALSE_MESSAGE(
472
Quaternion(0, 1, x, x).is_finite(),
473
"Quaternion with two components infinite should not be finite.");
474
475
CHECK_FALSE_MESSAGE(
476
Quaternion(0, x, x, x).is_finite(),
477
"Quaternion with three components infinite should not be finite.");
478
CHECK_FALSE_MESSAGE(
479
Quaternion(x, 1, x, x).is_finite(),
480
"Quaternion with three components infinite should not be finite.");
481
CHECK_FALSE_MESSAGE(
482
Quaternion(x, x, 2, x).is_finite(),
483
"Quaternion with three components infinite should not be finite.");
484
CHECK_FALSE_MESSAGE(
485
Quaternion(x, x, x, 3).is_finite(),
486
"Quaternion with three components infinite should not be finite.");
487
488
CHECK_FALSE_MESSAGE(
489
Quaternion(x, x, x, x).is_finite(),
490
"Quaternion with four components infinite should not be finite.");
491
}
492
493
} // namespace TestQuaternion
494
495