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
140
141
142 RotationOrder(final RotationStage stage1, final RotationStage stage2, final RotationStage stage3) {
143 this.name = stage1.name() + stage2.name() + stage3.name();
144 this.stage1 = stage1;
145 this.stage2 = stage2;
146 this.stage3 = stage3;
147
148 if (stage1 == stage3) {
149
150 if (stage1 != RotationStage.X && stage2 != RotationStage.X) {
151 missing = RotationStage.X;
152 } else if (stage1 != RotationStage.Y && stage2 != RotationStage.Y) {
153 missing = RotationStage.Y;
154 } else {
155 missing = RotationStage.Z;
156 }
157 } else {
158
159 missing = null;
160 }
161
162
163 final Vector3D a1 = stage1.getAxis();
164 final Vector3D a2 = stage2.getAxis();
165 final Vector3D a3 = missing == null ? stage3.getAxis() : missing.getAxis();
166 sign = FastMath.copySign(1.0, Vector3D.dotProduct(a3, Vector3D.crossProduct(a1, a2)));
167
168 }
169
170
171
172
173 @Override
174 public String toString() {
175 return name;
176 }
177
178
179
180
181 public Vector3D getA1() {
182 return stage1.getAxis();
183 }
184
185
186
187
188 public Vector3D getA2() {
189 return stage2.getAxis();
190 }
191
192
193
194
195 public Vector3D getA3() {
196 return stage3.getAxis();
197 }
198
199
200
201
202
203
204 public static RotationOrder getRotationOrder(final String value) {
205 try {
206 return RotationOrder.valueOf(value);
207 } catch (IllegalArgumentException iae) {
208
209 throw new MathIllegalStateException(iae,
210 LocalizedGeometryFormats.INVALID_ROTATION_ORDER_NAME,
211 value);
212 }
213 }
214
215
216
217
218
219
220
221 public double[] getAngles(final Rotation rotation, final RotationConvention convention) {
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237 final Vector3D vCol = getColumnVector(rotation, convention);
238 final Vector3D vRow = getRowVector(rotation, convention);
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266 if (missing == null) {
267
268
269 if (convention == RotationConvention.FRAME_TRANSFORM) {
270 final double s = stage2.getComponent(vRow) * -sign;
271 final double c = stage3.getComponent(vRow);
272 if (s * s + c * c > 1.0e-30) {
273
274 return new double[] {
275 FastMath.atan2(s, c),
276 safeAsin(stage1.getComponent(vRow), stage2.getComponent(vRow), stage3.getComponent(vRow)) * sign,
277 FastMath.atan2(stage2.getComponent(vCol) * -sign, stage1.getComponent(vCol))
278 };
279 } else {
280
281 final Vector3D midCol = rotation.applyTo(stage2.getAxis());
282 return new double[] {
283 0.0,
284 FastMath.copySign(MathUtils.SEMI_PI, stage1.getComponent(vRow) * sign),
285 FastMath.atan2(stage1.getComponent(midCol) * sign, stage2.getComponent(midCol))
286 };
287 }
288 } else {
289 final double s = stage2.getComponent(vCol) * -sign;
290 final double c = stage3.getComponent(vCol);
291 if (s * s + c * c > 1.0e-30) {
292
293 return new double[] {
294 FastMath.atan2(s, c),
295 safeAsin(stage3.getComponent(vRow), stage1.getComponent(vRow), stage2.getComponent(vRow)) * sign,
296 FastMath.atan2(stage2.getComponent(vRow) * -sign, stage1.getComponent(vRow))
297 };
298 } else {
299
300 final Vector3D midRow = rotation.applyInverseTo(stage2.getAxis());
301 return new double[] {
302 0.0,
303 FastMath.copySign(MathUtils.SEMI_PI, stage3.getComponent(vRow) * sign),
304 FastMath.atan2(stage1.getComponent(midRow) * sign, stage2.getComponent(midRow))
305 };
306 }
307 }
308 } else {
309
310 if (convention == RotationConvention.FRAME_TRANSFORM) {
311 final double s = stage2.getComponent(vRow);
312 final double c = missing.getComponent(vRow) * -sign;
313 if (s * s + c * c > 1.0e-30) {
314
315 return new double[] {
316 FastMath.atan2(s, c),
317 safeAcos(stage1.getComponent(vRow), stage2.getComponent(vRow), missing.getComponent(vRow)),
318 FastMath.atan2(stage2.getComponent(vCol), missing.getComponent(vCol) * sign)
319 };
320 } else {
321
322 final Vector3D midCol = rotation.applyTo(stage2.getAxis());
323 return new double[] {
324 FastMath.atan2(missing.getComponent(midCol) * -sign, stage2.getComponent(midCol)) *
325 FastMath.copySign(1, stage1.getComponent(vRow)),
326 stage1.getComponent(vRow) > 0 ? 0 : FastMath.PI,
327 0.0
328 };
329 }
330 } else {
331 final double s = stage2.getComponent(vCol);
332 final double c = missing.getComponent(vCol) * -sign;
333 if (s * s + c * c > 1.0e-30) {
334
335 return new double[] {
336 FastMath.atan2(s, c),
337 safeAcos(stage1.getComponent(vRow), stage2.getComponent(vRow), missing.getComponent(vRow)),
338 FastMath.atan2(stage2.getComponent(vRow), missing.getComponent(vRow) * sign)
339 };
340 } else {
341
342 final Vector3D midRow = rotation.applyInverseTo(stage2.getAxis());
343 return new double[] {
344 FastMath.atan2(missing.getComponent(midRow) * -sign, stage2.getComponent(midRow)) *
345 FastMath.copySign(1, stage1.getComponent(vRow)),
346 stage1.getComponent(vRow) > 0 ? 0 : FastMath.PI,
347 0.0
348 };
349 }
350 }
351 }
352 }
353
354
355
356
357
358
359
360
361 public <T extends CalculusFieldElement<T>> T[] getAngles(final FieldRotation<T> rotation, final RotationConvention convention) {
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377 final FieldVector3D<T> vCol = getColumnVector(rotation, convention);
378 final FieldVector3D<T> vRow = getRowVector(rotation, convention);
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406 if (missing == null) {
407
408 if (convention == RotationConvention.FRAME_TRANSFORM) {
409 final T s = stage2.getComponent(vRow).multiply(-sign);
410 final T c = stage3.getComponent(vRow);
411 if (s.square().add(c.square()).getReal() > 1.0e-30) {
412
413 return buildArray(FastMath.atan2(s, c),
414 safeAsin(stage1.getComponent(vRow), stage2.getComponent(vRow), stage3.getComponent(vRow)).multiply(sign),
415 FastMath.atan2(stage2.getComponent(vCol).multiply(-sign), stage1.getComponent(vCol)));
416 } else {
417
418 final FieldVector3D<T> midCol = rotation.applyTo(stage2.getAxis());
419 return buildArray(s.getField().getZero(),
420 FastMath.copySign(s.getPi().multiply(0.5), stage1.getComponent(vRow).multiply(sign)),
421 FastMath.atan2(stage1.getComponent(midCol).multiply(sign), stage2.getComponent(midCol)));
422 }
423 } else {
424 final T s = stage2.getComponent(vCol).multiply(-sign);
425 final T c = stage3.getComponent(vCol);
426 if (s.square().add(c.square()).getReal() > 1.0e-30) {
427
428 return buildArray(FastMath.atan2(stage2.getComponent(vCol).multiply(-sign), stage3.getComponent(vCol)),
429 safeAsin(stage3.getComponent(vRow), stage1.getComponent(vRow), stage2.getComponent(vRow)).multiply(sign),
430 FastMath.atan2(stage2.getComponent(vRow).multiply(-sign), stage1.getComponent(vRow)));
431 } else {
432
433 final FieldVector3D<T> midRow = rotation.applyInverseTo(stage2.getAxis());
434 return buildArray(s.getField().getZero(),
435 FastMath.copySign(s.getPi().multiply(0.5), stage3.getComponent(vRow).multiply(sign)),
436 FastMath.atan2(stage1.getComponent(midRow).multiply(sign), stage2.getComponent(midRow)));
437 }
438 }
439 } else {
440
441 if (convention == RotationConvention.FRAME_TRANSFORM) {
442 final T s = stage2.getComponent(vRow);
443 final T c = missing.getComponent(vRow).multiply(-sign);
444 if (s.square().add(c.square()).getReal() > 1.0e-30) {
445
446 return buildArray(FastMath.atan2(s, c),
447 safeAcos(stage1.getComponent(vRow), stage2.getComponent(vRow), missing.getComponent(vRow)),
448 FastMath.atan2(stage2.getComponent(vCol), missing.getComponent(vCol).multiply(sign)));
449 } else {
450
451 final FieldVector3D<T> midCol = rotation.applyTo(stage2.getAxis());
452 return buildArray(FastMath.atan2(missing.getComponent(midCol).multiply(-sign), stage2.getComponent(midCol)).
453 multiply(FastMath.copySign(1, stage1.getComponent(vRow).getReal())),
454 stage1.getComponent(vRow).getReal() > 0 ? s.getField().getZero() : s.getPi(),
455 s.getField().getZero());
456 }
457 } else {
458 final T s = stage2.getComponent(vCol);
459 final T c = missing.getComponent(vCol).multiply(-sign);
460 if (s.square().add(c.square()).getReal() > 1.0e-30) {
461
462 return buildArray(FastMath.atan2(s, c),
463 safeAcos(stage1.getComponent(vRow), stage2.getComponent(vRow), missing.getComponent(vRow)),
464 FastMath.atan2(stage2.getComponent(vRow), missing.getComponent(vRow).multiply(sign)));
465 } else {
466
467 final FieldVector3D<T> midRow = rotation.applyInverseTo(stage2.getAxis());
468 return buildArray(FastMath.atan2(missing.getComponent(midRow).multiply(-sign), stage2.getComponent(midRow)).
469 multiply(FastMath.copySign(1, stage1.getComponent(vRow).getReal())),
470 stage1.getComponent(vRow).getReal() > 0 ? s.getField().getZero() : s.getPi(),
471 s.getField().getZero());
472 }
473 }
474 }
475 }
476
477
478
479
480
481
482
483 private Vector3D getColumnVector(final Rotation rotation,
484 final RotationConvention convention) {
485 return rotation.applyTo(convention == RotationConvention.VECTOR_OPERATOR ?
486 stage3.getAxis() :
487 stage1.getAxis());
488 }
489
490
491
492
493
494
495
496 private Vector3D getRowVector(final Rotation rotation,
497 final RotationConvention convention) {
498 return rotation.applyInverseTo(convention == RotationConvention.VECTOR_OPERATOR ?
499 stage1.getAxis() :
500 stage3.getAxis());
501 }
502
503
504
505
506
507
508
509
510 private <T extends CalculusFieldElement<T>> FieldVector3D<T> getColumnVector(final FieldRotation<T> rotation,
511 final RotationConvention convention) {
512 return rotation.applyTo(convention == RotationConvention.VECTOR_OPERATOR ?
513 stage3.getAxis() :
514 stage1.getAxis());
515 }
516
517
518
519
520
521
522
523
524 private <T extends CalculusFieldElement<T>> FieldVector3D<T> getRowVector(final FieldRotation<T> rotation,
525 final RotationConvention convention) {
526 return rotation.applyInverseTo(convention == RotationConvention.VECTOR_OPERATOR ?
527 stage1.getAxis() :
528 stage3.getAxis());
529 }
530
531
532
533
534
535
536
537
538 private static double safeAcos(final double cos, final double sin1, final double sin2) {
539 if (cos < -SAFE_SWITCH) {
540 return FastMath.PI - FastMath.asin(FastMath.sqrt(sin1 * sin1 + sin2 * sin2));
541 } else if (cos > SAFE_SWITCH) {
542 return FastMath.asin(FastMath.sqrt(sin1 * sin1 + sin2 * sin2));
543 } else {
544 return FastMath.acos(cos);
545 }
546 }
547
548
549
550
551
552
553
554
555 private static double safeAsin(final double sin, final double cos1, final double cos2) {
556 if (sin < -SAFE_SWITCH) {
557 return -FastMath.acos(FastMath.sqrt(cos1 * cos1 + cos2 * cos2));
558 } else if (sin > SAFE_SWITCH) {
559 return FastMath.acos(FastMath.sqrt(cos1 * cos1 + cos2 * cos2));
560 } else {
561 return FastMath.asin(sin);
562 }
563 }
564
565
566
567
568
569
570
571
572
573 private static <T extends CalculusFieldElement<T>> T safeAcos(final T cos,
574 final T sin1,
575 final T sin2) {
576 if (cos.getReal() < -SAFE_SWITCH) {
577 return FastMath.asin(FastMath.sqrt(sin1.square().add(sin2.square()))).subtract(sin1.getPi()).negate();
578 } else if (cos.getReal() > SAFE_SWITCH) {
579 return FastMath.asin(FastMath.sqrt(sin1.square().add(sin2.square())));
580 } else {
581 return FastMath.acos(cos);
582 }
583 }
584
585
586
587
588
589
590
591
592
593 private static <T extends CalculusFieldElement<T>> T safeAsin(final T sin,
594 final T cos1,
595 final T cos2) {
596 if (sin.getReal() < -SAFE_SWITCH) {
597 return FastMath.acos(FastMath.sqrt(cos1.square().add(cos2.square()))).negate();
598 } else if (sin.getReal() > SAFE_SWITCH) {
599 return FastMath.acos(FastMath.sqrt(cos1.square().add(cos2.square())));
600 } else {
601 return FastMath.asin(sin);
602 }
603 }
604
605
606
607
608
609
610
611
612
613 private static <T extends CalculusFieldElement<T>> T[] buildArray(final T a0, final T a1, final T a2) {
614 final T[] array = MathArrays.buildArray(a0.getField(), 3);
615 array[0] = a0;
616 array[1] = a1;
617 array[2] = a2;
618 return array;
619 }
620
621 }