1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.numbers.quaternion;
18
19 import java.util.Random;
20 import java.util.SplittableRandom;
21 import org.junit.jupiter.api.Assertions;
22 import org.junit.jupiter.api.Test;
23
24 class QuaternionTest {
25
26 private static final double EPS = Math.ulp(1d);
27
28 private static final double COMPARISON_EPS = 1e-14;
29
30 @Test
31 void testZeroQuaternion() {
32 Assertions.assertEquals(0, Quaternion.ZERO.norm());
33 }
34
35 @Test
36 void testUnitQuaternions() {
37 Assertions.assertEquals(1, Quaternion.ONE.norm());
38 Assertions.assertSame(Quaternion.ONE, Quaternion.ONE.normalize());
39
40 Assertions.assertEquals(1, Quaternion.I.norm());
41 Assertions.assertSame(Quaternion.I, Quaternion.I.normalize());
42
43 Assertions.assertEquals(1, Quaternion.J.norm());
44 Assertions.assertSame(Quaternion.J, Quaternion.J.normalize());
45
46 Assertions.assertEquals(1, Quaternion.K.norm());
47 Assertions.assertSame(Quaternion.K, Quaternion.K.normalize());
48 }
49
50 @Test
51 final void testAccessors1() {
52 final double q0 = 2;
53 final double q1 = 5.4;
54 final double q2 = 17;
55 final double q3 = 0.0005;
56 final Quaternion q = Quaternion.of(q0, q1, q2, q3);
57
58 Assertions.assertEquals(q0, q.getW());
59 Assertions.assertEquals(q1, q.getX());
60 Assertions.assertEquals(q2, q.getY());
61 Assertions.assertEquals(q3, q.getZ());
62 }
63
64 @Test
65 final void testAccessors2() {
66 final double q0 = 2;
67 final double q1 = 5.4;
68 final double q2 = 17;
69 final double q3 = 0.0005;
70 final Quaternion q = Quaternion.of(q0, q1, q2, q3);
71
72 final double sP = q.getScalarPart();
73 final double[] vP = q.getVectorPart();
74
75 Assertions.assertEquals(q0, sP);
76 Assertions.assertEquals(q1, vP[0]);
77 Assertions.assertEquals(q2, vP[1]);
78 Assertions.assertEquals(q3, vP[2]);
79 }
80
81 @Test
82 final void testAccessors3() {
83 final double q0 = 2;
84 final double q1 = 5.4;
85 final double q2 = 17;
86 final double q3 = 0.0005;
87 final Quaternion q = Quaternion.of(q0, new double[] {q1, q2, q3});
88
89 final double sP = q.getScalarPart();
90 final double[] vP = q.getVectorPart();
91
92 Assertions.assertEquals(q0, sP);
93 Assertions.assertEquals(q1, vP[0]);
94 Assertions.assertEquals(q2, vP[1]);
95 Assertions.assertEquals(q3, vP[2]);
96 }
97
98 @Test
99 void testWrongDimension() {
100 Assertions.assertThrows(IllegalArgumentException.class,
101 () -> Quaternion.of(new double[] {1, 2})
102 );
103 }
104
105 @Test
106 final void testConjugate() {
107 final double q0 = 2;
108 final double q1 = 5.4;
109 final double q2 = 17;
110 final double q3 = 0.0005;
111 final Quaternion q = Quaternion.of(q0, q1, q2, q3);
112
113 final Quaternion qConjugate = q.conjugate();
114
115 Assertions.assertEquals(q0, qConjugate.getW());
116 Assertions.assertEquals(-q1, qConjugate.getX());
117 Assertions.assertEquals(-q2, qConjugate.getY());
118 Assertions.assertEquals(-q3, qConjugate.getZ());
119 }
120
121 @Test
122 final void testMultiplyQuaternionQuaternion() {
123
124
125
126 final Quaternion qA = Quaternion.of(1, 0.5, -3, 4);
127 final Quaternion qB = Quaternion.of(6, 2, 1, -9);
128 final Quaternion qResult = Quaternion.multiply(qA, qB);
129
130 Assertions.assertEquals(44, qResult.getW(), EPS);
131 Assertions.assertEquals(28, qResult.getX(), EPS);
132 Assertions.assertEquals(-4.5, qResult.getY(), EPS);
133 Assertions.assertEquals(21.5, qResult.getZ(), EPS);
134
135
136
137
138 final Quaternion productOfConjugate = qB.conjugate().multiply(qA.conjugate());
139 final Quaternion conjugateOfProduct = (qA.multiply(qB)).conjugate();
140
141 Assertions.assertEquals(productOfConjugate.getW(), conjugateOfProduct.getW(), EPS);
142 Assertions.assertEquals(productOfConjugate.getX(), conjugateOfProduct.getX(), EPS);
143 Assertions.assertEquals(productOfConjugate.getY(), conjugateOfProduct.getY(), EPS);
144 Assertions.assertEquals(productOfConjugate.getZ(), conjugateOfProduct.getZ(), EPS);
145 }
146
147 @Test
148 final void testMultiplyQuaternionVector() {
149
150
151
152 final Quaternion quaternion = Quaternion.of(4, 7, -1, 2);
153 final double[] vector = {2.0, 1.0, 3.0};
154 final Quaternion qResultQxV = Quaternion.multiply(quaternion, Quaternion.of(vector));
155
156 Assertions.assertEquals(-19, qResultQxV.getW(), EPS);
157 Assertions.assertEquals(3, qResultQxV.getX(), EPS);
158 Assertions.assertEquals(-13, qResultQxV.getY(), EPS);
159 Assertions.assertEquals(21, qResultQxV.getZ(), EPS);
160
161
162
163 final Quaternion qResultVxQ = Quaternion.multiply(Quaternion.of(vector), quaternion);
164
165 Assertions.assertEquals(-19, qResultVxQ.getW(), EPS);
166 Assertions.assertEquals(13, qResultVxQ.getX(), EPS);
167 Assertions.assertEquals(21, qResultVxQ.getY(), EPS);
168 Assertions.assertEquals(3, qResultVxQ.getZ(), EPS);
169 }
170
171 @Test
172 final void testDotProductQuaternionQuaternion() {
173
174 final double expected = -6.;
175
176 final Quaternion q1 = Quaternion.of(1, 2, 2, 1);
177 final Quaternion q2 = Quaternion.of(3, -2, -1, -3);
178
179 final double actual1 = Quaternion.dot(q1, q2);
180 final double actual2 = q1.dot(q2);
181
182 Assertions.assertEquals(expected, actual1, EPS);
183 Assertions.assertEquals(expected, actual2, EPS);
184 }
185
186 @Test
187 final void testScalarMultiplyDouble() {
188
189 final double w = 1.6;
190 final double x = -4.8;
191 final double y = 11.20;
192 final double z = 2.56;
193
194 final Quaternion q1 = Quaternion.of(0.5, -1.5, 3.5, 0.8);
195 final double a = 3.2;
196
197 final Quaternion q = q1.multiply(a);
198
199 Assertions.assertEquals(w, q.getW(), COMPARISON_EPS);
200 Assertions.assertEquals(x, q.getX(), COMPARISON_EPS);
201 Assertions.assertEquals(y, q.getY(), COMPARISON_EPS);
202 Assertions.assertEquals(z, q.getZ(), COMPARISON_EPS);
203 }
204
205 @Test
206 final void testAddQuaternionQuaternion() {
207
208 final double w = 4;
209 final double x = -1;
210 final double y = 2;
211 final double z = -4;
212
213 final Quaternion q1 = Quaternion.of(1., 2., -2., -1.);
214 final Quaternion q2 = Quaternion.of(3., -3., 4., -3.);
215
216 final Quaternion qa = Quaternion.add(q1, q2);
217 final Quaternion qb = q1.add(q2);
218
219 Assertions.assertEquals(w, qa.getW(), EPS);
220 Assertions.assertEquals(x, qa.getX(), EPS);
221 Assertions.assertEquals(y, qa.getY(), EPS);
222 Assertions.assertEquals(z, qa.getZ(), EPS);
223
224 Assertions.assertEquals(w, qb.getW(), EPS);
225 Assertions.assertEquals(x, qb.getX(), EPS);
226 Assertions.assertEquals(y, qb.getY(), EPS);
227 Assertions.assertEquals(z, qb.getZ(), EPS);
228 }
229
230 @Test
231 final void testSubtractQuaternionQuaternion() {
232
233 final double w = -2.;
234 final double x = 5.;
235 final double y = -6.;
236 final double z = 2.;
237
238 final Quaternion q1 = Quaternion.of(1., 2., -2., -1.);
239 final Quaternion q2 = Quaternion.of(3., -3., 4., -3.);
240
241 final Quaternion qa = Quaternion.subtract(q1, q2);
242 final Quaternion qb = q1.subtract(q2);
243
244 Assertions.assertEquals(w, qa.getW(), EPS);
245 Assertions.assertEquals(x, qa.getX(), EPS);
246 Assertions.assertEquals(y, qa.getY(), EPS);
247 Assertions.assertEquals(z, qa.getZ(), EPS);
248
249 Assertions.assertEquals(w, qb.getW(), EPS);
250 Assertions.assertEquals(x, qb.getX(), EPS);
251 Assertions.assertEquals(y, qb.getY(), EPS);
252 Assertions.assertEquals(z, qb.getZ(), EPS);
253 }
254
255 @Test
256 final void testNorm() {
257
258 final double q0 = 2;
259 final double q1 = 1;
260 final double q2 = -4;
261 final double q3 = 3;
262 final Quaternion q = Quaternion.of(q0, q1, q2, q3);
263
264 final double norm = q.norm();
265
266 Assertions.assertEquals(Math.sqrt(30), norm);
267
268 final double normSquareRef = Quaternion.multiply(q, q.conjugate()).getScalarPart();
269 Assertions.assertEquals(Math.sqrt(normSquareRef), norm);
270 }
271
272 @Test
273 final void testNormalize() {
274
275 final Quaternion q = Quaternion.of(2, 1, -4, -2);
276
277 final Quaternion versor = q.normalize();
278
279 Assertions.assertEquals(2.0 / 5.0, versor.getW());
280 Assertions.assertEquals(1.0 / 5.0, versor.getX());
281 Assertions.assertEquals(-4.0 / 5.0, versor.getY());
282 Assertions.assertEquals(-2.0 / 5.0, versor.getZ());
283
284 Assertions.assertEquals(1, versor.norm());
285
286 Assertions.assertSame(versor.normalize(), versor);
287 }
288
289 @Test
290 final void testNormalizeFail_zero() {
291 final Quaternion q = Quaternion.of(0, 0, 0, 0);
292 Assertions.assertThrows(IllegalStateException.class,
293 q::normalize
294 );
295 }
296
297 @Test
298 final void testNormalizeFail_nan() {
299 final Quaternion q = Quaternion.of(0, 0, 0, Double.NaN);
300 Assertions.assertThrows(IllegalStateException.class,
301 q::normalize
302 );
303
304 }
305
306 @Test
307 final void testNormalizeFail_positiveInfinity() {
308 final Quaternion q = Quaternion.of(0, 0, Double.POSITIVE_INFINITY, 0);
309 Assertions.assertThrows(IllegalStateException.class,
310 q::normalize
311 );
312 }
313
314 @Test
315 final void testNormalizeFail_negativeInfinity() {
316 final Quaternion q = Quaternion.of(0, Double.NEGATIVE_INFINITY, 0, 0);
317 Assertions.assertThrows(IllegalStateException.class,
318 q::normalize
319 );
320 }
321
322 @Test
323 final void testObjectEquals() {
324 final double one = 1;
325 final Quaternion q1 = Quaternion.of(one, one, one, one);
326 Assertions.assertEquals(q1, q1);
327
328 final Quaternion q2 = Quaternion.of(one, one, one, one);
329 Assertions.assertEquals(q2, q1);
330
331 final Quaternion q3 = Quaternion.of(one, Math.nextUp(one), one, one);
332 Assertions.assertNotEquals(q3, q1);
333
334 Assertions.assertNotEquals(q3, "bar");
335 }
336
337 @Test
338 void testHashCode() {
339 Quaternion x = Quaternion.of(0.0, 0.0, 0.0, 0.0);
340 Quaternion y = Quaternion.of(0.0, 0.0 + Double.MIN_VALUE, 0.0, 0.0);
341 Assertions.assertNotEquals(x.hashCode(), y.hashCode());
342 y = Quaternion.of(0.0 + Double.MIN_VALUE, 0.0, 0.0, 0.0);
343 Assertions.assertNotEquals(x.hashCode(), y.hashCode());
344
345
346
347 final String msg = "'equals' not compatible with 'hashCode'";
348
349 x = Quaternion.of(0.0, 0.0, 0.0, 0.0);
350 y = Quaternion.of(-0.0, 0.0, 0.0, 0.0);
351 Assertions.assertNotEquals(x.hashCode(), y.hashCode());
352 Assertions.assertNotEquals(x, y, msg);
353
354 x = Quaternion.of(0.0, 0.0, 0.0, 0.0);
355 y = Quaternion.of(0.0, -0.0, 0.0, 0.0);
356 Assertions.assertNotEquals(x.hashCode(), y.hashCode());
357 Assertions.assertNotEquals(x, y, msg);
358
359 x = Quaternion.of(0.0, 0.0, 0.0, 0.0);
360 y = Quaternion.of(0.0, 0.0, -0.0, 0.0);
361 Assertions.assertNotEquals(x.hashCode(), y.hashCode());
362 Assertions.assertNotEquals(x, y, msg);
363
364 x = Quaternion.of(0.0, 0.0, 0.0, 0.0);
365 y = Quaternion.of(0.0, 0.0, 0.0, -0.0);
366 Assertions.assertNotEquals(x.hashCode(), y.hashCode());
367 Assertions.assertNotEquals(x, y, msg);
368 }
369
370 @Test
371 final void testQuaternionEquals() {
372 final double inc = 1e-5;
373 final Quaternion q1 = Quaternion.of(2, 1, -4, -2);
374 final Quaternion q2 = Quaternion.of(q1.getW() + inc, q1.getX(), q1.getY(), q1.getZ());
375 final Quaternion q3 = Quaternion.of(q1.getW(), q1.getX() + inc, q1.getY(), q1.getZ());
376 final Quaternion q4 = Quaternion.of(q1.getW(), q1.getX(), q1.getY() + inc, q1.getZ());
377 final Quaternion q5 = Quaternion.of(q1.getW(), q1.getX(), q1.getY(), q1.getZ() + inc);
378
379 Assertions.assertFalse(q1.equals(q2, 0.9 * inc));
380 Assertions.assertFalse(q1.equals(q3, 0.9 * inc));
381 Assertions.assertFalse(q1.equals(q4, 0.9 * inc));
382 Assertions.assertFalse(q1.equals(q5, 0.9 * inc));
383
384 Assertions.assertTrue(q1.equals(q2, 1.1 * inc));
385 Assertions.assertTrue(q1.equals(q3, 1.1 * inc));
386 Assertions.assertTrue(q1.equals(q4, 1.1 * inc));
387 Assertions.assertTrue(q1.equals(q5, 1.1 * inc));
388 }
389
390 @Test
391 final void testQuaternionEquals2() {
392 final Quaternion q1 = Quaternion.of(1, 4, 2, 3);
393 final double gap = 1e-5;
394 final Quaternion q2 = Quaternion.of(1 + gap, 4 + gap, 2 + gap, 3 + gap);
395
396 Assertions.assertTrue(q1.equals(q2, 10 * gap));
397 Assertions.assertFalse(q1.equals(q2, gap));
398 Assertions.assertFalse(q1.equals(q2, gap / 10));
399 }
400
401 @Test
402 final void testIsUnit() {
403 final Random r = new Random(48);
404 final int numberOfTrials = 1000;
405 for (int i = 0; i < numberOfTrials; i++) {
406 final Quaternion q1 = Quaternion.of(r.nextDouble(), r.nextDouble(), r.nextDouble(), r.nextDouble());
407 final Quaternion q2 = q1.normalize();
408 Assertions.assertTrue(q2.isUnit(COMPARISON_EPS));
409 }
410
411 final Quaternion q = Quaternion.of(1, 1, 1, 1);
412 Assertions.assertFalse(q.isUnit(COMPARISON_EPS));
413 }
414
415 @Test
416 final void testIsPure() {
417 final Quaternion q1 = Quaternion.of(0, 5, 4, 8);
418 Assertions.assertTrue(q1.isPure(EPS));
419
420 final Quaternion q2 = Quaternion.of(0 - EPS, 5, 4, 8);
421 Assertions.assertTrue(q2.isPure(EPS));
422
423 final Quaternion q3 = Quaternion.of(0 - 1.1 * EPS, 5, 4, 8);
424 Assertions.assertFalse(q3.isPure(EPS));
425
426 final Random r = new Random(48);
427 final double[] v = {r.nextDouble(), r.nextDouble(), r.nextDouble()};
428 final Quaternion q4 = Quaternion.of(v);
429 Assertions.assertTrue(q4.isPure(0));
430
431 final Quaternion q5 = Quaternion.of(0, v);
432 Assertions.assertTrue(q5.isPure(0));
433 }
434
435 @Test
436 final void testPositivePolarFormWhenScalarPositive() {
437 Quaternion q = Quaternion.of(3, -3, -3, 3).positivePolarForm();
438 Quaternion expected = Quaternion.of(0.5, -0.5, -0.5, 0.5);
439 assertEquals(q, expected, EPS);
440
441 Assertions.assertSame(q.positivePolarForm(), q);
442 }
443
444 @Test
445 final void testPositivePolarFormWhenScalarNegative() {
446 Quaternion q = Quaternion.of(-3, 3, -3, 3).positivePolarForm();
447 Quaternion expected = Quaternion.of(0.5, -0.5, 0.5, -0.5);
448 assertEquals(q, expected, EPS);
449
450 Assertions.assertSame(q.positivePolarForm(), q);
451 }
452
453 @Test
454 final void testPositivePolarFormWhenScalarPositiveAndNormalized() {
455 Quaternion q = Quaternion.of(123, 45, 67, 89).normalize().positivePolarForm();
456
457 Assertions.assertTrue(q.getW() >= 0);
458 Assertions.assertSame(q.positivePolarForm(), q);
459 }
460
461 @Test
462 final void testPositivePolarFormWhenScalarNegativeAndNormalized() {
463 Quaternion q = Quaternion.of(123, 45, 67, 89).normalize().negate().positivePolarForm();
464
465 Assertions.assertTrue(q.getW() >= 0);
466 Assertions.assertSame(q.positivePolarForm(), q);
467 }
468
469 @Test
470 void testNegate() {
471 final double a = -1;
472 final double b = 2;
473 final double c = -3;
474 final double d = 4;
475 final Quaternion q = Quaternion.of(a, b, c, d);
476 final Quaternion qNeg = q.negate();
477 Assertions.assertEquals(-a, qNeg.getW());
478 Assertions.assertEquals(-b, qNeg.getX());
479 Assertions.assertEquals(-c, qNeg.getY());
480 Assertions.assertEquals(-d, qNeg.getZ());
481
482 Assertions.assertTrue(q.equals(qNeg.negate(), 0d));
483 }
484
485 @Test
486 void testNegateNormalized() {
487 final double a = -1;
488 final double b = 2;
489 final double c = -3;
490 final double d = 4;
491 final Quaternion q = Quaternion.of(a, b, c, d).normalize();
492 final Quaternion qNeg = q.negate();
493 Assertions.assertTrue(q.equals(qNeg.negate(), 0d));
494 }
495
496 @Test
497 void testNegatePositivePolarForm() {
498 final double a = -1;
499 final double b = 2;
500 final double c = -3;
501 final double d = 4;
502 final Quaternion q = Quaternion.of(a, b, c, d).positivePolarForm();
503 final Quaternion qNeg = q.negate();
504 Assertions.assertTrue(q.equals(qNeg.negate(), 0d));
505 }
506
507 @Test
508 final void testPolarForm() {
509 final SplittableRandom r = new SplittableRandom(48);
510 final int numberOfTrials = 1000;
511 for (int i = 0; i < numberOfTrials; i++) {
512 final Quaternion q = Quaternion.of(2 * (r.nextDouble() - 0.5), 2 * (r.nextDouble() - 0.5),
513 2 * (r.nextDouble() - 0.5), 2 * (r.nextDouble() - 0.5));
514 final Quaternion qP = q.positivePolarForm();
515
516 Assertions.assertTrue(qP.isUnit(COMPARISON_EPS), "polar form is not unit length");
517 Assertions.assertTrue(qP.getW() >= 0, "scalar part is not positive");
518 }
519 }
520
521 @Test
522 final void testInverse() {
523 final Quaternion q = Quaternion.of(1.5, 4, 2, -2.5);
524
525 final Quaternion inverseQ = q.inverse();
526 Assertions.assertEquals(1.5 / 28.5, inverseQ.getW());
527 Assertions.assertEquals(-4.0 / 28.5, inverseQ.getX());
528 Assertions.assertEquals(-2.0 / 28.5, inverseQ.getY());
529 Assertions.assertEquals(2.5 / 28.5, inverseQ.getZ());
530
531 final Quaternion product = Quaternion.multiply(inverseQ, q);
532 Assertions.assertEquals(1, product.getW(), EPS);
533 Assertions.assertEquals(0, product.getX(), EPS);
534 Assertions.assertEquals(0, product.getY(), EPS);
535 Assertions.assertEquals(0, product.getZ(), EPS);
536
537 final Quaternion qNul = Quaternion.of(0, 0, 0, 0);
538 try {
539 final Quaternion inverseQNul = qNul.inverse();
540 Assertions.fail("expecting ZeroException but got : " + inverseQNul);
541 } catch (IllegalStateException ex) {
542
543 }
544 }
545
546 @Test
547 void testInverse_zeroNorm() {
548 Quaternion q = Quaternion.of(0, 0, 0, 0);
549 Assertions.assertThrows(IllegalStateException.class,
550 q::inverse
551 );
552 }
553
554 @Test
555 void testInverse_nanNorm() {
556 Quaternion q = Quaternion.of(Double.NaN, 0, 0, 0);
557 Assertions.assertThrows(IllegalStateException.class,
558 q::inverse
559 );
560 }
561
562 @Test
563 void testInverse_positiveInfinityNorm() {
564 Quaternion q = Quaternion.of(0, Double.POSITIVE_INFINITY, 0, 0);
565 Assertions.assertThrows(IllegalStateException.class,
566 q::inverse
567 );
568 }
569
570 @Test
571 void testInverse_negativeInfinityNorm() {
572 Quaternion q = Quaternion.of(0, 0, Double.NEGATIVE_INFINITY, 0);
573 Assertions.assertThrows(IllegalStateException.class,
574 q::inverse
575 );
576 }
577
578 @Test
579 void testInverseNormalized() {
580 final Quaternion invQ = Quaternion.of(-1.2, 3.4, -5.6, -7.8).normalize().inverse();
581 final Quaternion q = invQ.inverse();
582 final Quaternion result = q.multiply(invQ);
583 Assertions.assertTrue(Quaternion.ONE.equals(result, EPS), result.toString());
584 }
585
586 @Test
587 void testInversePositivePolarForm() {
588 final Quaternion invQ = Quaternion.of(1.2, -3.4, 5.6, -7.8).positivePolarForm().inverse();
589 final Quaternion q = invQ.inverse();
590 final Quaternion result = q.multiply(invQ);
591 Assertions.assertTrue(Quaternion.ONE.equals(result, EPS), result.toString());
592 }
593
594 @Test
595 final void testMultiply() {
596 final Quaternion q1 = Quaternion.of(1, 2, 3, 4);
597 final Quaternion q2 = Quaternion.of(4, 3, 2, 1);
598 final Quaternion actual = q1.multiply(q2);
599 final double w = 1 * 4 - 2 * 3 - 3 * 2 - 4 * 1;
600 final double x = 1 * 3 + 2 * 4 + 3 * 1 - 4 * 2;
601 final double y = 1 * 2 - 2 * 1 + 3 * 4 + 4 * 3;
602 final double z = 1 * 1 + 2 * 2 - 3 * 3 + 4 * 4;
603 final Quaternion expected = Quaternion.of(w, x, y, z);
604 assertEquals(actual, expected, EPS);
605 }
606
607 @Test
608 final void testParseFromToString() {
609 final Quaternion q = Quaternion.of(1.1, 2.2, 3.3, 4.4);
610 Quaternion parsed = Quaternion.parse(q.toString());
611 assertEquals(parsed, q, EPS);
612 }
613
614 @Test
615 final void testParseSpecials() {
616 Quaternion parsed = Quaternion.parse("[1e-5 Infinity NaN -0xa.cp0]");
617 Assertions.assertEquals(1e-5, parsed.getW(), EPS);
618 Assertions.assertTrue(Double.isInfinite(parsed.getX()));
619 Assertions.assertTrue(Double.isNaN(parsed.getY()));
620 Assertions.assertEquals(-0xa.cp0, parsed.getZ(), EPS);
621 }
622
623 @Test
624 final void testParseMissingStart() {
625 Assertions.assertThrows(IllegalArgumentException.class,
626 () -> Quaternion.parse("1.0 2.0 3.0 4.0]")
627 );
628 }
629
630 @Test
631 final void testParseMissingEnd() {
632 Assertions.assertThrows(IllegalArgumentException.class,
633 () -> Quaternion.parse("[1.0 2.0 3.0 4.0")
634 );
635 }
636
637 @Test
638 final void testParseMissingPart() {
639 Assertions.assertThrows(IllegalArgumentException.class,
640 () -> Quaternion.parse("[1.0 2.0 3.0 ]")
641 );
642 }
643
644 @Test
645 final void testParseInvalidScalar() {
646 Assertions.assertThrows(IllegalArgumentException.class,
647 () -> Quaternion.parse("[1.x 2.0 3.0 4.0]")
648 );
649 }
650
651 @Test
652 final void testParseInvalidI() {
653 Assertions.assertThrows(IllegalArgumentException.class,
654 () -> Quaternion.parse("[1.0 2.0x 3.0 4.0]")
655 );
656 }
657
658 @Test
659 final void testParseInvalidJ() {
660 Assertions.assertThrows(IllegalArgumentException.class,
661 () -> Quaternion.parse("[1.0 2.0 3.0x 4.0]")
662 );
663 }
664
665 @Test
666 final void testParseInvalidK() {
667 Assertions.assertThrows(IllegalArgumentException.class,
668 () -> Quaternion.parse("[1.0 2.0 3.0 4.0x]")
669 );
670 }
671
672 @Test
673 final void testToString() {
674 final Quaternion q = Quaternion.of(1, 2, 3, 4);
675 Assertions.assertEquals("[1.0 2.0 3.0 4.0]", q.toString());
676 }
677
678
679
680
681
682
683
684 private void assertEquals(Quaternion actual, Quaternion expected, double tolerance) {
685 Assertions.assertTrue(actual.equals(expected, tolerance), "expecting " + expected + " but got " + actual);
686 }
687
688 }