|
| 1 | +r""" |
| 2 | +========================================= |
| 3 | + Plot Paths In Rotation and Vector Space |
| 4 | +========================================= |
| 5 | +This example shows how paths though either rotation or vector space |
| 6 | +can be plotted using ORIX. These are the shortest paths through their |
| 7 | +respective spaces, and thus not always straight lines in euclidean |
| 8 | +projections (axis-angle, stereographic, etc.). |
| 9 | +This functionality is available in :class:`~orix.vector.Vector3d`, |
| 10 | +:class:`~orix.quaternions.Rotation`, |
| 11 | +:class:`~orix.quaternions.Orientation`, |
| 12 | +and :class:`~orix.quaternions.Misorientation`. |
| 13 | +""" |
| 14 | + |
| 15 | +from matplotlib import cm |
| 16 | +import matplotlib.pyplot as plt |
| 17 | +import numpy as np |
| 18 | + |
| 19 | +from orix.plot import register_projections |
| 20 | +from orix.plot.direction_color_keys import DirectionColorKeyTSL |
| 21 | +from orix.quaternion import Orientation, OrientationRegion, Quaternion |
| 22 | +from orix.quaternion.symmetry import C1, Oh |
| 23 | +from orix.sampling import sample_S2 |
| 24 | +from orix.vector import Vector3d |
| 25 | + |
| 26 | +plt.close("all") |
| 27 | +register_projections() # Register our custom Matplotlib projections |
| 28 | +np.random.seed(2319) # Create reproducible random data |
| 29 | + |
| 30 | + |
| 31 | +fig = plt.figure(figsize=(6, 6)) |
| 32 | +n_steps = 30 |
| 33 | + |
| 34 | +# ========= # |
| 35 | +# Example 1: Plotting multiple paths into a user defined axis |
| 36 | +# ========= # |
| 37 | +# This subplot shows several paths through the cubic (m3m) fundamental zone |
| 38 | +# created by rotating 20 randomly chosen points 30 degrees around the z axis. |
| 39 | +# these paths are drawn in rodrigues space, which is an equal-angle projection |
| 40 | +# of rotation space. As such, notice how all lines tracing out axial rotations |
| 41 | +# are straight, but lines starting closer to the center of the fundamental zone |
| 42 | +# appear shorter. |
| 43 | +# the sampe paths are then also plotted on an Inverse Pole Figure (IPF) plot. |
| 44 | +rod_ax = fig.add_subplot(2, 2, 1, projection="rodrigues", proj_type="ortho") |
| 45 | +ipf_ax = fig.add_subplot(2, 2, 2, projection="ipf", symmetry=Oh) |
| 46 | + |
| 47 | +# 10 random orientations with the cubic m3m ('Oh' in the schoenflies notation) |
| 48 | +# crystal symmetry. |
| 49 | +oris = Orientation( |
| 50 | + data=np.array( |
| 51 | + [ |
| 52 | + [0.69, 0.24, 0.68, 0.01], |
| 53 | + [0.26, 0.59, 0.32, 0.7], |
| 54 | + [0.07, 0.17, 0.93, 0.31], |
| 55 | + [0.6, 0.03, 0.61, 0.52], |
| 56 | + [0.51, 0.38, 0.34, 0.69], |
| 57 | + [0.31, 0.86, 0.22, 0.35], |
| 58 | + [0.68, 0.67, 0.06, 0.31], |
| 59 | + [0.01, 0.12, 0.05, 0.99], |
| 60 | + [0.39, 0.45, 0.34, 0.72], |
| 61 | + [0.65, 0.59, 0.46, 0.15], |
| 62 | + ] |
| 63 | + ), |
| 64 | + symmetry=Oh, |
| 65 | +) |
| 66 | +# reduce them to their crystallographically identical representations |
| 67 | +oris = oris.reduce() |
| 68 | +# define a 20 degree rotation around the z axis |
| 69 | +shift = Orientation.from_axes_angles([0, 0, 1], 30, degrees=True) |
| 70 | +# for each orientation, calculate and plot the path they would take during a |
| 71 | +# 45 degree shift. |
| 72 | +segment_colors = cm.inferno(np.linspace(0, 1, n_steps)) |
| 73 | +for ori in oris: |
| 74 | + points = Orientation.stack([ori, (shift * ori)]).reduce() |
| 75 | + points.symmetry = Oh |
| 76 | + path = Orientation.from_path_ends(points, steps=n_steps) |
| 77 | + rod_ax.scatter(path, c=segment_colors) |
| 78 | + ipf_ax.scatter(path, c=segment_colors) |
| 79 | + |
| 80 | +# add the wireframe and clean up the plot. |
| 81 | +fz = OrientationRegion.from_symmetry(path.symmetry) |
| 82 | +rod_ax.plot_wireframe(fz) |
| 83 | +rod_ax._correct_aspect_ratio(fz) |
| 84 | +rod_ax.axis("off") |
| 85 | +rod_ax.set_title(r"Rodrigues, multiple paths") |
| 86 | +ipf_ax.set_title(r"IPF, multiple paths ") |
| 87 | + |
| 88 | + |
| 89 | +# %% |
| 90 | +# ========= # |
| 91 | +# Example 2: Plotting a path using `Rotation.scatter' |
| 92 | +# ========= # |
| 93 | +# This subplot traces the path of an object rotated 90 degrees around the |
| 94 | +# X axis, then 90 degrees around the Y axis. |
| 95 | +rots = Orientation.from_axes_angles( |
| 96 | + [[1, 0, 0], [1, 0, 0], [0, 1, 0]], [0, 90, 90], degrees=True, symmetry=C1 |
| 97 | +) |
| 98 | +rots[2] = rots[1] * rots[2] |
| 99 | +path = Orientation.from_path_ends(rots, steps=n_steps) |
| 100 | +# create a list of RGBA color values for a gradient red line and blue line |
| 101 | +path_colors = np.vstack( |
| 102 | + [cm.Reds(np.linspace(0.5, 1, n_steps)), cm.Blues(np.linspace(0.5, 1, n_steps))] |
| 103 | +) |
| 104 | + |
| 105 | +# Here, we instead use the in-built plotting tool from |
| 106 | +# Orientation.scatter to auto-generate the subplot. This is especially handy when |
| 107 | +# plotting only a single Orientation object. |
| 108 | +path.scatter(figure=fig, position=[2, 2, 3], marker=">", c=path_colors) |
| 109 | +fig.axes[2].set_title(r"Axis-Angle, two $90^\circ$ rotations") |
| 110 | + |
| 111 | +# %% |
| 112 | + |
| 113 | +# ========= # |
| 114 | +# Example 3: paths in stereographic plots |
| 115 | +# ========= # |
| 116 | +# This is similar to the second example, but now vectors are being rotated |
| 117 | +# 30 degrees around the [1,1,1] axis on a stereographic plot. |
| 118 | + |
| 119 | +vec_ax = plt.subplot(2, 2, 4, projection="stereographic", hemisphere="upper") |
| 120 | +ipf_colormap = DirectionColorKeyTSL(C1) |
| 121 | + |
| 122 | +# define a mesh of vectors with approximately 20 degree spacing, and |
| 123 | +# within 80 degrees of the Z axis |
| 124 | +vecs = sample_S2(20) |
| 125 | +vecs = vecs[vecs.polar < (80 * np.pi / 180)] |
| 126 | + |
| 127 | +# define a 15 degree rotation around [1,1,1] |
| 128 | +rots = Quaternion.from_axes_angles([1, 1, 1], [0, 15], degrees=True) |
| 129 | + |
| 130 | +for vec in vecs: |
| 131 | + path_ends = rots * vec |
| 132 | + # color each path using a gradient pased on the IPF coloring. |
| 133 | + c = ipf_colormap.direction2color(vec) |
| 134 | + if np.abs(path_ends.cross(path_ends[::-1])[0].norm) > 1e-12: |
| 135 | + path = Vector3d.from_path_ends(path_ends, steps=100) |
| 136 | + segment_c = c * np.linspace(0.25, 1, path.size)[:, np.newaxis] |
| 137 | + vec_ax.scatter(path, c=segment_c) |
| 138 | + else: |
| 139 | + vec_ax.scatter(path_ends[0], c=c) |
| 140 | + |
| 141 | +vec_ax.set_title(r"Stereographic") |
| 142 | +vec_ax.set_labels("X", "Y") |
| 143 | +plt.tight_layout() |
0 commit comments