|
| 1 | +r""" |
| 2 | +=================== |
| 3 | +Combining rotations |
| 4 | +=================== |
| 5 | +
|
| 6 | +This example demonstrates how to combine two rotations :math:`g_A` and |
| 7 | +:math:`g_B`, i.e. from left to right like so |
| 8 | +
|
| 9 | +.. math:: |
| 10 | + g_{AB} = g_A \cdot g_B. |
| 11 | +
|
| 12 | +This order follows from the convention of passive rotations chosen in |
| 13 | +orix which follows :cite:`rowenhorst2015consistent`. |
| 14 | +
|
| 15 | +To convince ourselves that this order is correct, we will reproduce the |
| 16 | +example given by Rowenhorst and co-workers in section 4.2.2 in the above |
| 17 | +mentioned paper. We want to rotate a vector :math:`(0, 0, z)` by two |
| 18 | +rotations: rotation :math:`A` by :math:`120^{\circ}` around |
| 19 | +:math:`[1 1 1]`, and rotation :math:`B` by :math:`180^{\circ}` around |
| 20 | +:math:`[1 1 0]`; rotation :math:`A` will be carried out first, followed |
| 21 | +by rotation :math:`B`. |
| 22 | +
|
| 23 | +Note that a negative angle when *defining* a rotation in the axis-angle |
| 24 | +representation is necessary for consistent transformations between |
| 25 | +rotation representations. The rotation still rotates a vector |
| 26 | +intuitively. |
| 27 | +""" |
| 28 | + |
| 29 | +import matplotlib.pyplot as plt |
| 30 | + |
| 31 | +from orix import plot |
| 32 | +from orix.quaternion import Rotation |
| 33 | +from orix.vector import Vector3d |
| 34 | + |
| 35 | +plt.rcParams.update({"font.size": 12, "grid.alpha": 0.5}) |
| 36 | + |
| 37 | +gA = Rotation.from_axes_angles([1, 1, 1], -120, degrees=True) |
| 38 | +gB = Rotation.from_axes_angles([1, 1, 0], -180, degrees=True) |
| 39 | +gAB = gA * gB |
| 40 | + |
| 41 | +# Compare with quaternions and orientation matrices from section 4.2.2 |
| 42 | +# in Rowenhorst et al. (2015) |
| 43 | +g_all = Rotation.stack((gA, gB, gAB)).squeeze() |
| 44 | +print("gA, gB and gAB:\n* As quaternions:\n", g_all) |
| 45 | +print("* As orientation matrices:\n", g_all.to_matrix().squeeze().round(10)) |
| 46 | + |
| 47 | +v_start = Vector3d.zvector() |
| 48 | +v_end = gAB * v_start |
| 49 | +print( |
| 50 | + "Point rotated by gAB:\n", |
| 51 | + v_start.data.squeeze().tolist(), |
| 52 | + "->", |
| 53 | + v_end.data.squeeze().round(10).tolist(), |
| 54 | +) |
| 55 | + |
| 56 | +# Illustrate the steps of the rotation by plotting the vector before |
| 57 | +# (red), during (green) and after (blue) the rotation and the rotation |
| 58 | +# paths (first: cyan; second: magenta) |
| 59 | +v_intermediate = gB * v_start |
| 60 | + |
| 61 | +v_si_path = Vector3d.from_path_ends(Vector3d.stack((v_start, v_intermediate))) |
| 62 | +v_sie_path = Vector3d.from_path_ends(Vector3d.stack((v_intermediate, v_end))) |
| 63 | + |
| 64 | +fig = plt.figure(layout="tight") |
| 65 | +ax0 = fig.add_subplot(121, projection="stereographic", hemisphere="upper") |
| 66 | +ax1 = fig.add_subplot(122, projection="stereographic", hemisphere="lower") |
| 67 | +ax0.stereographic_grid(), ax1.stereographic_grid() |
| 68 | +Vector3d.stack((v_start, v_intermediate, v_end)).scatter( |
| 69 | + figure=fig, |
| 70 | + s=50, |
| 71 | + c=["r", "g", "b"], |
| 72 | + axes_labels=["e1", "e2"], |
| 73 | +) |
| 74 | +ax0.plot(v_si_path, color="c"), ax1.plot(v_si_path, color="c") |
| 75 | +ax0.plot(v_sie_path, color="m"), ax1.plot(v_sie_path, color="m") |
| 76 | +gA.axis.scatter(figure=fig, c="orange") |
| 77 | +gB.axis.scatter(figure=fig, c="k") |
| 78 | +text_kw = dict(bbox=dict(alpha=0.5, fc="w", boxstyle="round,pad=0.1"), ha="right") |
| 79 | +ax0.text(v_start, s="Start", **text_kw) |
| 80 | +ax1.text(v_intermediate, s="Intermediate", **text_kw) |
| 81 | +ax1.text(v_end, s="End", **text_kw) |
| 82 | +ax1.text(gA.axis, s="Axis gA", **text_kw) |
| 83 | +ax0.text(gB.axis, s="Axis gB", **text_kw) |
0 commit comments