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)
|
||||
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):
|
||||
"""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):
|
||||
|
|
|
@ -974,6 +974,45 @@ class TestRotation:
|
|||
R_2 = Rotation.from_Euler_angles([360,0,0],degrees=True)
|
||||
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])
|
||||
def test_average(self,angle):
|
||||
R = Rotation.from_axis_angle([[0,0,1,10],[0,0,1,angle]],degrees=True)
|
||||
|
|
Loading…
Reference in New Issue