compatible with scipy.spatial.transform.Rotation
also introduced inplace variants and '/' as multiplicative inverse of '*'
This commit is contained in:
parent
98723cb0ed
commit
f48a446353
|
@ -144,10 +144,91 @@ class Rotation:
|
||||||
p = self.quaternion[...,1:]/np.linalg.norm(self.quaternion[...,1:],axis=-1,keepdims=True)
|
p = self.quaternion[...,1:]/np.linalg.norm(self.quaternion[...,1:],axis=-1,keepdims=True)
|
||||||
return self.copy(rotation=Rotation(np.block([np.cos(pwr*phi),np.sin(pwr*phi)*p]))._standardize())
|
return self.copy(rotation=Rotation(np.block([np.cos(pwr*phi),np.sin(pwr*phi)*p]))._standardize())
|
||||||
|
|
||||||
|
def __ipow__(self,pwr):
|
||||||
|
"""
|
||||||
|
Raise quaternion to power (in-place).
|
||||||
|
|
||||||
|
Equivalent to performing the rotation 'pwr' times.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
pwr : float
|
||||||
|
Power to raise quaternion to.
|
||||||
|
|
||||||
|
"""
|
||||||
|
return self**pwr
|
||||||
|
|
||||||
|
|
||||||
def __mul__(self,other):
|
def __mul__(self,other):
|
||||||
"""Standard multiplication is not implemented."""
|
"""
|
||||||
raise NotImplementedError('Use "R@b", i.e. matmul, to apply rotation "R" to object "b"')
|
Compose this rotation with other.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
other : damask.Rotation of shape(self.shape)
|
||||||
|
Rotation for comosition.
|
||||||
|
|
||||||
|
"""
|
||||||
|
if isinstance(other,Rotation):
|
||||||
|
return self@other
|
||||||
|
else:
|
||||||
|
raise TypeError('Use "R@b", i.e. matmul, to apply rotation "R" to object "b"')
|
||||||
|
|
||||||
|
def __imul__(self,other):
|
||||||
|
"""
|
||||||
|
Compose this rotation with other (in-place).
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
other : damask.Rotation of shape(self.shape)
|
||||||
|
Rotation for comosition.
|
||||||
|
|
||||||
|
"""
|
||||||
|
return self*other
|
||||||
|
|
||||||
|
|
||||||
|
def __truediv__(self,other):
|
||||||
|
"""
|
||||||
|
Compose this rotation with inverse of other.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
other : damask.Rotation of shape (self.shape)
|
||||||
|
Rotation to inverse composition.
|
||||||
|
|
||||||
|
"""
|
||||||
|
if isinstance(other,Rotation):
|
||||||
|
return self@~other
|
||||||
|
else:
|
||||||
|
raise TypeError('Use "R@b", i.e. matmul, to apply rotation "R" to object "b"')
|
||||||
|
|
||||||
|
def __itruediv__(self,other):
|
||||||
|
"""
|
||||||
|
Compose this rotation with inverse of other (in-place).
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
other : damask.Rotation of shape (self.shape)
|
||||||
|
Rotation to inverse composition.
|
||||||
|
|
||||||
|
"""
|
||||||
|
return self/other
|
||||||
|
|
||||||
|
|
||||||
|
def apply(self,other):
|
||||||
|
"""
|
||||||
|
Apply rotation to vector or second/forth order tensor field.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
other : numpy.ndarray of shape (...,3), (...,3,3), or (...,3,3,3,3)
|
||||||
|
Vector or tensor on which the rotation is apply
|
||||||
|
|
||||||
|
"""
|
||||||
|
if isinstance(other,np.ndarray):
|
||||||
|
return self@other
|
||||||
|
else:
|
||||||
|
raise TypeError('Use "R1*R2" or "R1/R2", to compose rotations')
|
||||||
|
|
||||||
|
|
||||||
def __matmul__(self,other):
|
def __matmul__(self,other):
|
||||||
|
|
|
@ -974,6 +974,45 @@ class TestRotation:
|
||||||
R_2 = Rotation.from_Euler_angles([360,0,0],degrees=True)
|
R_2 = Rotation.from_Euler_angles([360,0,0],degrees=True)
|
||||||
assert np.allclose(R_1.misorientation(R_2).as_matrix(),np.eye(3))
|
assert np.allclose(R_1.misorientation(R_2).as_matrix(),np.eye(3))
|
||||||
|
|
||||||
|
def test_composition(self):
|
||||||
|
a,b = (Rotation.from_random(),Rotation.from_random())
|
||||||
|
c = a * b
|
||||||
|
a *= b
|
||||||
|
assert c == a
|
||||||
|
|
||||||
|
def test_composition_invalid(self):
|
||||||
|
with pytest.raises(TypeError):
|
||||||
|
Rotation()*np.ones(3)
|
||||||
|
|
||||||
|
def test_composition_inverse(self):
|
||||||
|
a,b = (Rotation.from_random(),Rotation.from_random())
|
||||||
|
c = a / b
|
||||||
|
a /= b
|
||||||
|
assert c == a
|
||||||
|
|
||||||
|
def test_composition_inverse_invalid(self):
|
||||||
|
with pytest.raises(TypeError):
|
||||||
|
Rotation()/np.ones(3)
|
||||||
|
|
||||||
|
def test_power(self):
|
||||||
|
a = Rotation.from_random()
|
||||||
|
r = (np.random.rand()-.5)*4
|
||||||
|
b = a**r
|
||||||
|
a **= r
|
||||||
|
assert a == b
|
||||||
|
|
||||||
|
def test_invariant(self):
|
||||||
|
R = Rotation.from_random()
|
||||||
|
assert R/R == R*R**(-1) == Rotation()
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('vec',[np.ones(3),np.ones((3,3)), np.ones((3,3,3,3))])
|
||||||
|
def test_apply(self,vec):
|
||||||
|
assert (Rotation().from_random().apply(vec)).all()
|
||||||
|
|
||||||
|
def test_apply_invalid(self):
|
||||||
|
with pytest.raises(TypeError):
|
||||||
|
Rotation().apply(Rotation())
|
||||||
|
|
||||||
@pytest.mark.parametrize('angle',[10,20,30,40,50,60,70,80,90,100,120])
|
@pytest.mark.parametrize('angle',[10,20,30,40,50,60,70,80,90,100,120])
|
||||||
def test_average(self,angle):
|
def test_average(self,angle):
|
||||||
R = Rotation.from_axis_angle([[0,0,1,10],[0,0,1,angle]],degrees=True)
|
R = Rotation.from_axis_angle([[0,0,1,10],[0,0,1,angle]],degrees=True)
|
||||||
|
|
Loading…
Reference in New Issue