1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 package org.hipparchus.geometry.euclidean.threed;
24
25 import org.hipparchus.CalculusFieldElement;
26 import org.hipparchus.exception.MathIllegalStateException;
27 import org.hipparchus.geometry.LocalizedGeometryFormats;
28 import org.hipparchus.util.FastMath;
29 import org.hipparchus.util.MathArrays;
30 import org.hipparchus.util.MathUtils;
31
32
33
34
35
36
37 public enum RotationOrder {
38
39
40
41
42
43 XYZ(RotationStage.X, RotationStage.Y, RotationStage.Z),
44
45
46
47
48
49 XZY(RotationStage.X, RotationStage.Z, RotationStage.Y),
50
51
52
53
54
55 YXZ(RotationStage.Y, RotationStage.X, RotationStage.Z),
56
57
58
59
60
61 YZX(RotationStage.Y, RotationStage.Z, RotationStage.X),
62
63
64
65
66
67 ZXY(RotationStage.Z, RotationStage.X, RotationStage.Y),
68
69
70
71
72
73 ZYX(RotationStage.Z, RotationStage.Y, RotationStage.X),
74
75
76
77
78
79 XYX(RotationStage.X, RotationStage.Y, RotationStage.X),
80
81
82
83
84
85 XZX(RotationStage.X, RotationStage.Z, RotationStage.X),
86
87
88
89
90
91 YXY(RotationStage.Y, RotationStage.X, RotationStage.Y),
92
93
94
95
96
97 YZY(RotationStage.Y, RotationStage.Z, RotationStage.Y),
98
99
100
101
102
103 ZXZ(RotationStage.Z, RotationStage.X, RotationStage.Z),
104
105
106
107
108
109 ZYZ(RotationStage.Z, RotationStage.Y, RotationStage.Z);
110
111
112
113
114 private static final double SAFE_SWITCH = 0.999;
115
116
117 private final String name;
118
119
120 private final RotationStage stage1;
121
122
123 private final RotationStage stage2;
124
125
126 private final RotationStage stage3;
127
128
129 private final RotationStage missing;
130
131
132 private final double sign;
133
134
135
136
137
138
139 RotationOrder(final RotationStage stage1, final RotationStage stage2, final RotationStage stage3) {
140 this.name = stage1.name() + stage2.name() + stage3.name();
141 this.stage1 = stage1;
142 this.stage2 = stage2;
143 this.stage3 = stage3;
144
145 if (stage1 == stage3) {
146
147 if (stage1 != RotationStage.X && stage2 != RotationStage.X) {
148 missing = RotationStage.X;
149 } else if (stage1 != RotationStage.Y && stage2 != RotationStage.Y) {
150 missing = RotationStage.Y;
151 } else {
152 missing = RotationStage.Z;
153 }
154 } else {
155
156 missing = null;
157 }
158
159
160 final Vector3D a1 = stage1.getAxis();
161 final Vector3D a2 = stage2.getAxis();
162 final Vector3D a3 = missing == null ? stage3.getAxis() : missing.getAxis();
163 sign = FastMath.copySign(1.0, Vector3D.dotProduct(a3, Vector3D.crossProduct(a1, a2)));
164
165 }
166
167
168
169
170 @Override
171 public String toString() {
172 return name;
173 }
174
175
176
177
178 public Vector3D getA1() {
179 return stage1.getAxis();
180 }
181
182
183
184
185 public Vector3D getA2() {
186 return stage2.getAxis();
187 }
188
189
190
191
192 public Vector3D getA3() {
193 return stage3.getAxis();
194 }
195
196
197
198
199
200
201 public static RotationOrder getRotationOrder(final String value) {
202 try {
203 return RotationOrder.valueOf(value);
204 } catch (IllegalArgumentException iae) {
205
206 throw new MathIllegalStateException(iae,
207 LocalizedGeometryFormats.INVALID_ROTATION_ORDER_NAME,
208 value);
209 }
210 }
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228 public double[] getAngles(final Rotation rotation, final RotationConvention convention) {
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244 final Vector3D vCol = getColumnVector(rotation, convention);
245 final Vector3D vRow = getRowVector(rotation, convention);
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279 if (missing == null) {
280
281
282 if (convention == RotationConvention.FRAME_TRANSFORM) {
283 final double s = stage2.getComponent(vRow) * -sign;
284 final double c = stage3.getComponent(vRow);
285 if (s * s + c * c > 1.0e-30) {
286
287 return new double[] {
288 FastMath.atan2(s, c),
289 safeAsin(stage1.getComponent(vRow), stage2.getComponent(vRow), stage3.getComponent(vRow)) * sign,
290 FastMath.atan2(stage2.getComponent(vCol) * -sign, stage1.getComponent(vCol))
291 };
292 } else {
293
294 final Vector3D midCol = rotation.applyTo(stage2.getAxis());
295 return new double[] {
296 0.0,
297 FastMath.copySign(MathUtils.SEMI_PI, stage1.getComponent(vRow) * sign),
298 FastMath.atan2(stage1.getComponent(midCol) * sign, stage2.getComponent(midCol))
299 };
300 }
301 } else {
302 final double s = stage2.getComponent(vCol) * -sign;
303 final double c = stage3.getComponent(vCol);
304 if (s * s + c * c > 1.0e-30) {
305
306 return new double[] {
307 FastMath.atan2(s, c),
308 safeAsin(stage3.getComponent(vRow), stage1.getComponent(vRow), stage2.getComponent(vRow)) * sign,
309 FastMath.atan2(stage2.getComponent(vRow) * -sign, stage1.getComponent(vRow))
310 };
311 } else {
312
313 final Vector3D midRow = rotation.applyInverseTo(stage2.getAxis());
314 return new double[] {
315 0.0,
316 FastMath.copySign(MathUtils.SEMI_PI, stage3.getComponent(vRow) * sign),
317 FastMath.atan2(stage1.getComponent(midRow) * sign, stage2.getComponent(midRow))
318 };
319 }
320 }
321 } else {
322
323 if (convention == RotationConvention.FRAME_TRANSFORM) {
324 final double s = stage2.getComponent(vRow);
325 final double c = missing.getComponent(vRow) * -sign;
326 if (s * s + c * c > 1.0e-30) {
327
328 return new double[] {
329 FastMath.atan2(s, c),
330 safeAcos(stage1.getComponent(vRow), stage2.getComponent(vRow), missing.getComponent(vRow)),
331 FastMath.atan2(stage2.getComponent(vCol), missing.getComponent(vCol) * sign)
332 };
333 } else {
334
335 final Vector3D midCol = rotation.applyTo(stage2.getAxis());
336 return new double[] {
337 FastMath.atan2(missing.getComponent(midCol) * -sign, stage2.getComponent(midCol)) *
338 FastMath.copySign(1, stage1.getComponent(vRow)),
339 stage1.getComponent(vRow) > 0 ? 0 : FastMath.PI,
340 0.0
341 };
342 }
343 } else {
344 final double s = stage2.getComponent(vCol);
345 final double c = missing.getComponent(vCol) * -sign;
346 if (s * s + c * c > 1.0e-30) {
347
348 return new double[] {
349 FastMath.atan2(s, c),
350 safeAcos(stage1.getComponent(vRow), stage2.getComponent(vRow), missing.getComponent(vRow)),
351 FastMath.atan2(stage2.getComponent(vRow), missing.getComponent(vRow) * sign)
352 };
353 } else {
354
355 final Vector3D midRow = rotation.applyInverseTo(stage2.getAxis());
356 return new double[] {
357 FastMath.atan2(missing.getComponent(midRow) * -sign, stage2.getComponent(midRow)) *
358 FastMath.copySign(1, stage1.getComponent(vRow)),
359 stage1.getComponent(vRow) > 0 ? 0 : FastMath.PI,
360 0.0
361 };
362 }
363 }
364 }
365 }
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384 public <T extends CalculusFieldElement<T>> T[] getAngles(final FieldRotation<T> rotation, final RotationConvention convention) {
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400 final FieldVector3D<T> vCol = getColumnVector(rotation, convention);
401 final FieldVector3D<T> vRow = getRowVector(rotation, convention);
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435 if (missing == null) {
436
437 if (convention == RotationConvention.FRAME_TRANSFORM) {
438 final T s = stage2.getComponent(vRow).multiply(-sign);
439 final T c = stage3.getComponent(vRow);
440 if (s.square().add(c.square()).getReal() > 1.0e-30) {
441
442 return buildArray(FastMath.atan2(s, c),
443 safeAsin(stage1.getComponent(vRow), stage2.getComponent(vRow), stage3.getComponent(vRow)).multiply(sign),
444 FastMath.atan2(stage2.getComponent(vCol).multiply(-sign), stage1.getComponent(vCol)));
445 } else {
446
447 final FieldVector3D<T> midCol = rotation.applyTo(stage2.getAxis());
448 return buildArray(s.getField().getZero(),
449 FastMath.copySign(s.getPi().multiply(0.5), stage1.getComponent(vRow).multiply(sign)),
450 FastMath.atan2(stage1.getComponent(midCol).multiply(sign), stage2.getComponent(midCol)));
451 }
452 } else {
453 final T s = stage2.getComponent(vCol).multiply(-sign);
454 final T c = stage3.getComponent(vCol);
455 if (s.square().add(c.square()).getReal() > 1.0e-30) {
456
457 return buildArray(FastMath.atan2(stage2.getComponent(vCol).multiply(-sign), stage3.getComponent(vCol)),
458 safeAsin(stage3.getComponent(vRow), stage1.getComponent(vRow), stage2.getComponent(vRow)).multiply(sign),
459 FastMath.atan2(stage2.getComponent(vRow).multiply(-sign), stage1.getComponent(vRow)));
460 } else {
461
462 final FieldVector3D<T> midRow = rotation.applyInverseTo(stage2.getAxis());
463 return buildArray(s.getField().getZero(),
464 FastMath.copySign(s.getPi().multiply(0.5), stage3.getComponent(vRow).multiply(sign)),
465 FastMath.atan2(stage1.getComponent(midRow).multiply(sign), stage2.getComponent(midRow)));
466 }
467 }
468 } else {
469
470 if (convention == RotationConvention.FRAME_TRANSFORM) {
471 final T s = stage2.getComponent(vRow);
472 final T c = missing.getComponent(vRow).multiply(-sign);
473 if (s.square().add(c.square()).getReal() > 1.0e-30) {
474
475 return buildArray(FastMath.atan2(s, c),
476 safeAcos(stage1.getComponent(vRow), stage2.getComponent(vRow), missing.getComponent(vRow)),
477 FastMath.atan2(stage2.getComponent(vCol), missing.getComponent(vCol).multiply(sign)));
478 } else {
479
480 final FieldVector3D<T> midCol = rotation.applyTo(stage2.getAxis());
481 return buildArray(FastMath.atan2(missing.getComponent(midCol).multiply(-sign), stage2.getComponent(midCol)).
482 multiply(FastMath.copySign(1, stage1.getComponent(vRow).getReal())),
483 stage1.getComponent(vRow).getReal() > 0 ? s.getField().getZero() : s.getPi(),
484 s.getField().getZero());
485 }
486 } else {
487 final T s = stage2.getComponent(vCol);
488 final T c = missing.getComponent(vCol).multiply(-sign);
489 if (s.square().add(c.square()).getReal() > 1.0e-30) {
490
491 return buildArray(FastMath.atan2(s, c),
492 safeAcos(stage1.getComponent(vRow), stage2.getComponent(vRow), missing.getComponent(vRow)),
493 FastMath.atan2(stage2.getComponent(vRow), missing.getComponent(vRow).multiply(sign)));
494 } else {
495
496 final FieldVector3D<T> midRow = rotation.applyInverseTo(stage2.getAxis());
497 return buildArray(FastMath.atan2(missing.getComponent(midRow).multiply(-sign), stage2.getComponent(midRow)).
498 multiply(FastMath.copySign(1, stage1.getComponent(vRow).getReal())),
499 stage1.getComponent(vRow).getReal() > 0 ? s.getField().getZero() : s.getPi(),
500 s.getField().getZero());
501 }
502 }
503 }
504 }
505
506
507
508
509
510
511
512 private Vector3D getColumnVector(final Rotation rotation,
513 final RotationConvention convention) {
514 return rotation.applyTo(convention == RotationConvention.VECTOR_OPERATOR ?
515 stage3.getAxis() :
516 stage1.getAxis());
517 }
518
519
520
521
522
523
524
525 private Vector3D getRowVector(final Rotation rotation,
526 final RotationConvention convention) {
527 return rotation.applyInverseTo(convention == RotationConvention.VECTOR_OPERATOR ?
528 stage1.getAxis() :
529 stage3.getAxis());
530 }
531
532
533
534
535
536
537
538
539 private <T extends CalculusFieldElement<T>> FieldVector3D<T> getColumnVector(final FieldRotation<T> rotation,
540 final RotationConvention convention) {
541 return rotation.applyTo(convention == RotationConvention.VECTOR_OPERATOR ?
542 stage3.getAxis() :
543 stage1.getAxis());
544 }
545
546
547
548
549
550
551
552
553 private <T extends CalculusFieldElement<T>> FieldVector3D<T> getRowVector(final FieldRotation<T> rotation,
554 final RotationConvention convention) {
555 return rotation.applyInverseTo(convention == RotationConvention.VECTOR_OPERATOR ?
556 stage1.getAxis() :
557 stage3.getAxis());
558 }
559
560
561
562
563
564
565
566
567 private static double safeAcos(final double cos, final double sin1, final double sin2) {
568 if (cos < -SAFE_SWITCH) {
569 return FastMath.PI - FastMath.asin(FastMath.sqrt(sin1 * sin1 + sin2 * sin2));
570 } else if (cos > SAFE_SWITCH) {
571 return FastMath.asin(FastMath.sqrt(sin1 * sin1 + sin2 * sin2));
572 } else {
573 return FastMath.acos(cos);
574 }
575 }
576
577
578
579
580
581
582
583
584 private static double safeAsin(final double sin, final double cos1, final double cos2) {
585 if (sin < -SAFE_SWITCH) {
586 return -FastMath.acos(FastMath.sqrt(cos1 * cos1 + cos2 * cos2));
587 } else if (sin > SAFE_SWITCH) {
588 return FastMath.acos(FastMath.sqrt(cos1 * cos1 + cos2 * cos2));
589 } else {
590 return FastMath.asin(sin);
591 }
592 }
593
594
595
596
597
598
599
600
601
602 private static <T extends CalculusFieldElement<T>> T safeAcos(final T cos,
603 final T sin1,
604 final T sin2) {
605 if (cos.getReal() < -SAFE_SWITCH) {
606 return FastMath.asin(FastMath.sqrt(sin1.square().add(sin2.square()))).subtract(sin1.getPi()).negate();
607 } else if (cos.getReal() > SAFE_SWITCH) {
608 return FastMath.asin(FastMath.sqrt(sin1.square().add(sin2.square())));
609 } else {
610 return FastMath.acos(cos);
611 }
612 }
613
614
615
616
617
618
619
620
621
622 private static <T extends CalculusFieldElement<T>> T safeAsin(final T sin,
623 final T cos1,
624 final T cos2) {
625 if (sin.getReal() < -SAFE_SWITCH) {
626 return FastMath.acos(FastMath.sqrt(cos1.square().add(cos2.square()))).negate();
627 } else if (sin.getReal() > SAFE_SWITCH) {
628 return FastMath.acos(FastMath.sqrt(cos1.square().add(cos2.square())));
629 } else {
630 return FastMath.asin(sin);
631 }
632 }
633
634
635
636
637
638
639
640
641
642 private static <T extends CalculusFieldElement<T>> T[] buildArray(final T a0, final T a1, final T a2) {
643 final T[] array = MathArrays.buildArray(a0.getField(), 3);
644 array[0] = a0;
645 array[1] = a1;
646 array[2] = a2;
647 return array;
648 }
649
650 }