def from_euler(cls, euler): """Creates a rotation from an array of Euler angles. Parameters ---------- euler : array-like Euler angles in the Bunge convention. """ # Bunge convention euler = np.array(euler) n = euler.shape[:-1] alpha, beta, gamma = euler[..., 0], euler[..., 1], euler[..., 2] alpha -= np.pi / 2 gamma -= 3 * np.pi / 2 zero = np.zeros(n) qalpha = Quaternion( np.stack((np.cos(alpha / 2), zero, zero, np.sin(alpha / 2)), axis=-1)) qbeta = Quaternion( np.stack((np.cos(beta / 2), zero, np.sin(beta / 2), zero), axis=-1)) qgamma = Quaternion( np.stack((np.cos(gamma / 2), zero, zero, np.sin(gamma / 2)), axis=-1)) data = qalpha * qbeta * qgamma rot = cls(data.data) rot.improper = zero return rot
def __mul__(self, other): if isinstance(other, Rotation): q = Quaternion(self) * Quaternion(other) r = other.__class__(q) i = np.logical_xor(self.improper, other.improper) r.improper = i return r if isinstance(other, Quaternion): q = Quaternion(self) * other return q if isinstance(other, Vector3d): v = Quaternion(self) * other improper = (self.improper * np.ones(other.shape)).astype(bool) v[improper] = -v[improper] return v if isinstance(other, int) or isinstance(other, list): # has to plus/minus 1 other = np.atleast_1d(other).astype(int) if isinstance(other, np.ndarray): assert np.all( abs(other) == 1), "Rotations can only be multiplied by 1 or -1" r = Rotation(self.data) r.improper = np.logical_xor(self.improper, other == -1) return r return NotImplemented
def __gt__(self, other): """Overridden greater than method. Applying this to an Orientation will return only orientations those that lie within the OrientationRegion """ c = Quaternion(self).dot_outer(Quaternion(other)).data inside = np.logical_or( np.all(np.greater_equal(c, -EPSILON), axis=0), np.all(np.less_equal(c, +EPSILON), axis=0), ) return inside
def test_abcd(): quat = Quaternion([2, 2, 2, 2]) quat.a = 1 quat.b = 1 quat.c = 1 quat.d = 1 assert np.allclose(quat.data, 1)
def from_euler(cls, euler, convention="bunge", direction="crystal2lab"): """Creates a rotation from an array of Euler angles. Parameters ---------- euler : array-like Euler angles in the Bunge convention. convention : str Only 'bunge' is currently suppported direction : str 'lab2crystal' or 'crystal2lab' """ if convention != "bunge": raise ValuerError("Only 'bunge' is an acceptable convention") if direction not in ["lab2crystal", "crystal2lab"]: raise ValueError( "The chosen direction is not one of the allowed options") euler = np.array(euler) n = euler.shape[:-1] # Uses A.5 & A.6 from Modelling Simul. Mater. Sci. Eng. 23 (2015) 083501 alpha = euler[..., 0] # psi1 beta = euler[..., 1] # Psi gamma = euler[..., 2] # psi3 sigma = 0.5 * np.add(alpha, gamma) delta = 0.5 * np.subtract(alpha, gamma) c = np.cos(beta / 2) s = np.sin(beta / 2) # Using P = 1 from A.6 q = np.zeros(n + (4, )) q[..., 0] = c * np.cos(sigma) q[..., 1] = -s * np.cos(delta) q[..., 2] = -s * np.sin(delta) q[..., 3] = -c * np.sin(sigma) for i in [1, 2, 3, 0]: # flip the zero element last q[..., i] = np.where(q[..., 0] < 0, -q[..., i], q[..., i]) data = Quaternion(q) if direction == "lab2crystal": data = ~data rot = cls(data.data) rot.improper = np.zeros((n)) return rot
def from_matrix(cls, matrix): """Creates rotations from orientation matrices [Rowenhorst2015]_. Parameters ---------- matrix : array_like Array of orientation matrices. Examples -------- >>> import numpy as np >>> from orix.quaternion.rotation import Rotation >>> r = Rotation.from_matrix(np.eye(3)) >>> np.allclose(r.data, [1, 0, 0, 0]) True >>> r = Rotation.from_matrix(np.diag([1, -1, -1])) >>> np.allclose(r.data, [0, 1, 0, 0]) True """ om = np.asarray(matrix) # Assuming (3, 3) as last two dims n = (1, ) if om.ndim == 2 else om.shape[:-2] q = np.zeros(n + (4, )) # Compute quaternion components q0_almost = 1 + om[..., 0, 0] + om[..., 1, 1] + om[..., 2, 2] q1_almost = 1 + om[..., 0, 0] - om[..., 1, 1] - om[..., 2, 2] q2_almost = 1 - om[..., 0, 0] + om[..., 1, 1] - om[..., 2, 2] q3_almost = 1 - om[..., 0, 0] - om[..., 1, 1] + om[..., 2, 2] q[..., 0] = 0.5 * np.sqrt(np.where(q0_almost < _FLOAT_EPS, 0, q0_almost)) q[..., 1] = 0.5 * np.sqrt(np.where(q1_almost < _FLOAT_EPS, 0, q1_almost)) q[..., 2] = 0.5 * np.sqrt(np.where(q2_almost < _FLOAT_EPS, 0, q2_almost)) q[..., 3] = 0.5 * np.sqrt(np.where(q3_almost < _FLOAT_EPS, 0, q3_almost)) # Modify component signs if necessary q[..., 1] = np.where(om[..., 2, 1] < om[..., 1, 2], -q[..., 1], q[..., 1]) q[..., 2] = np.where(om[..., 0, 2] < om[..., 2, 0], -q[..., 2], q[..., 2]) q[..., 3] = np.where(om[..., 1, 0] < om[..., 0, 1], -q[..., 3], q[..., 3]) return cls(Quaternion(q)).unit # Normalized
def test_init(input_length): with pytest.raises(DimensionError): Quaternion(tuple(range(input_length)))
def something(request): return Quaternion(request.param)
def identity(): return Quaternion((1, 0, 0, 0))
def test_multiply_vector(quaternion, vector, expected): q = Quaternion(quaternion) v = Vector3d(vector) v_new = q * v assert np.allclose(v_new.data, expected)
def from_euler(cls, euler, convention="bunge", direction="crystal2lab"): """Creates a rotation from an array of Euler angles in radians. Parameters ---------- euler : array-like Euler angles in radians in the Bunge convention. convention : str Only "bunge" is supported for new data. direction : str "lab2crystal" or "crystal2lab". """ conventions = ["bunge", "Krakow_Hielscher"] if convention not in conventions: raise ValueError( f"The chosen convention is not one of the allowed options {conventions}" ) directions = ["lab2crystal", "crystal2lab"] if direction not in directions: raise ValueError( f"The chosen direction is not one of the allowed options {directions}" ) euler = np.array(euler) if np.any(np.abs(euler) > 9): warnings.warn( "Angles are assumed to be in radians, but degrees might have been " "passed") n = euler.shape[:-1] alpha, beta, gamma = euler[..., 0], euler[..., 1], euler[..., 2] if convention == "Krakow_Hielscher": # To be applied to the data found at: # https://www.repository.cam.ac.uk/handle/1810/263510 alpha -= np.pi / 2 gamma -= 3 * np.pi / 2 zero = np.zeros(n) qalpha = Quaternion( np.stack((np.cos(alpha / 2), zero, zero, np.sin(alpha / 2)), axis=-1)) qbeta = Quaternion( np.stack((np.cos(beta / 2), zero, np.sin(beta / 2), zero), axis=-1)) qgamma = Quaternion( np.stack((np.cos(gamma / 2), zero, zero, np.sin(gamma / 2)), axis=-1)) data = qalpha * qbeta * qgamma rot = cls(data.data) rot.improper = zero return rot elif convention == "bunge": # Uses A.5 & A.6 from Modelling Simul. Mater. Sci. Eng. 23 # (2015) 083501 sigma = 0.5 * np.add(alpha, gamma) delta = 0.5 * np.subtract(alpha, gamma) c = np.cos(beta / 2) s = np.sin(beta / 2) # Using P = 1 from A.6 q = np.zeros(n + (4, )) q[..., 0] = c * np.cos(sigma) q[..., 1] = -s * np.cos(delta) q[..., 2] = -s * np.sin(delta) q[..., 3] = -c * np.sin(sigma) for i in [1, 2, 3, 0]: # flip the zero element last q[..., i] = np.where(q[..., 0] < 0, -q[..., i], q[..., i]) data = Quaternion(q) if direction == "lab2crystal": data = ~data rot = cls(data.data) rot.improper = np.zeros((n)) return rot
def from_euler(cls, euler, convention="bunge", direction="crystal2lab"): """Creates a rotation from an array of Euler angles. Parameters ---------- euler : array-like Euler angles in the Bunge convention. convention : str Only 'bunge' is currently supported for new data direction : str 'lab2crystal' or 'crystal2lab' """ if convention not in ["bunge", "Krakow_Hielscher"]: raise ValueError("The chosen convention is not one of the allowed options") if direction not in ["lab2crystal", "crystal2lab"]: raise ValueError("The chosen direction is not one of the allowed options") if convention == "Krakow_Hielscher": # To be applied to the data found at: # https://www.repository.cam.ac.uk/handle/1810/263510 euler = np.array(euler) n = euler.shape[:-1] alpha, beta, gamma = euler[..., 0], euler[..., 1], euler[..., 2] alpha -= np.pi / 2 gamma -= 3 * np.pi / 2 zero = np.zeros(n) qalpha = Quaternion( np.stack((np.cos(alpha / 2), zero, zero, np.sin(alpha / 2)), axis=-1) ) qbeta = Quaternion( np.stack((np.cos(beta / 2), zero, np.sin(beta / 2), zero), axis=-1) ) qgamma = Quaternion( np.stack((np.cos(gamma / 2), zero, zero, np.sin(gamma / 2)), axis=-1) ) data = qalpha * qbeta * qgamma rot = cls(data.data) rot.improper = zero return rot elif convention == "bunge": euler = np.array(euler) n = euler.shape[:-1] # Uses A.5 & A.6 from Modelling Simul. Mater. Sci. Eng. 23 (2015) 083501 alpha = euler[..., 0] # psi1 beta = euler[..., 1] # Psi gamma = euler[..., 2] # psi3 sigma = 0.5 * np.add(alpha, gamma) delta = 0.5 * np.subtract(alpha, gamma) c = np.cos(beta / 2) s = np.sin(beta / 2) # Using P = 1 from A.6 q = np.zeros(n + (4,)) q[..., 0] = c * np.cos(sigma) q[..., 1] = -s * np.cos(delta) q[..., 2] = -s * np.sin(delta) q[..., 3] = -c * np.sin(sigma) for i in [1, 2, 3, 0]: # flip the zero element last q[..., i] = np.where(q[..., 0] < 0, -q[..., i], q[..., i]) data = Quaternion(q) if direction == "lab2crystal": data = ~data rot = cls(data.data) rot.improper = np.zeros((n)) return rot
def test_check_quat(): """ check is an oddly named function""" quat = Quaternion([2, 2, 2, 2]) assert np.allclose(quat.data, check_quaternion(quat).data)
def quaternion(request): return Quaternion(request.param)
import pytest from orix.base import DimensionError from orix.quaternion import Quaternion from orix.vector import Vector3d values = [ (0.707, 0., 0., 0.707), (0.5, -0.5, -0.5, 0.5), (0., 0., 0., 1.), (1., 1., 1., 1.), ( (0.5, -0.5, -0.5, 0.5), (0., 0., 0., 1.), ), Quaternion([ [(0., 0., 0., 1.), (0.707, 0., 0., 0.707), ], [(1., 1., 1., 1.), (0.707, 0., 0., 0.707), ] ]), np.array((4, 3, 2, 1)) ] @pytest.fixture(params=values) def quaternion(request): return Quaternion(request.param) @pytest.fixture def identity(): return Quaternion((1, 0, 0, 0))
def __gt__(self, other): c = Quaternion(self).dot_outer(Quaternion(other)).data inside = np.logical_or( np.all(np.greater_equal(c, 0), axis=0), np.all(np.less_equal(c, 0), axis=0) ) return inside
from orix.vector import Vector3d values = [ (0.707, 0.0, 0.0, 0.707), (0.5, -0.5, -0.5, 0.5), (0.0, 0.0, 0.0, 1.0), (1.0, 1.0, 1.0, 1.0), ( (0.5, -0.5, -0.5, 0.5), (0.0, 0.0, 0.0, 1.0), ), Quaternion([ [ (0.0, 0.0, 0.0, 1.0), (0.707, 0.0, 0.0, 0.707), ], [ (1.0, 1.0, 1.0, 1.0), (0.707, 0.0, 0.0, 0.707), ], ]), np.array((4, 3, 2, 1)), ] @pytest.fixture(params=values) def quaternion(request): return Quaternion(request.param) @pytest.fixture def identity():