added optional keyword `degrees´ to q.asEulers().
added methods inFZ, equivalentQuaternions, and equivalentOrientations to Orientation class. general polishing…
This commit is contained in:
parent
e310763c52
commit
2d8af638b0
|
@ -58,36 +58,36 @@ class Quaternion:
|
||||||
omega = math.acos(self.w)
|
omega = math.acos(self.w)
|
||||||
vRescale = math.sin(exponent*omega)/math.sin(omega)
|
vRescale = math.sin(exponent*omega)/math.sin(omega)
|
||||||
Q = Quaternion()
|
Q = Quaternion()
|
||||||
|
Q.w = math.cos(exponent*omega)
|
||||||
Q.x = self.x * vRescale
|
Q.x = self.x * vRescale
|
||||||
Q.y = self.y * vRescale
|
Q.y = self.y * vRescale
|
||||||
Q.z = self.z * vRescale
|
Q.z = self.z * vRescale
|
||||||
Q.w = math.cos(exponent*omega)
|
|
||||||
return Q
|
return Q
|
||||||
|
|
||||||
def __ipow__(self, exponent):
|
def __ipow__(self, exponent):
|
||||||
omega = math.acos(self.w)
|
omega = math.acos(self.w)
|
||||||
vRescale = math.sin(exponent*omega)/math.sin(omega)
|
vRescale = math.sin(exponent*omega)/math.sin(omega)
|
||||||
|
self.w = np.cos(exponent*omega)
|
||||||
self.x *= vRescale
|
self.x *= vRescale
|
||||||
self.y *= vRescale
|
self.y *= vRescale
|
||||||
self.z *= vRescale
|
self.z *= vRescale
|
||||||
self.w = np.cos(exponent*omega)
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def __mul__(self, other):
|
def __mul__(self, other):
|
||||||
try: # quaternion
|
try: # quaternion
|
||||||
|
Aw = self.w
|
||||||
Ax = self.x
|
Ax = self.x
|
||||||
Ay = self.y
|
Ay = self.y
|
||||||
Az = self.z
|
Az = self.z
|
||||||
Aw = self.w
|
Bw = other.w
|
||||||
Bx = other.x
|
Bx = other.x
|
||||||
By = other.y
|
By = other.y
|
||||||
Bz = other.z
|
Bz = other.z
|
||||||
Bw = other.w
|
|
||||||
Q = Quaternion()
|
Q = Quaternion()
|
||||||
|
Q.w = - Ax * Bx - Ay * By - Az * Bz + Aw * Bw
|
||||||
Q.x = + Ax * Bw + Ay * Bz - Az * By + Aw * Bx
|
Q.x = + Ax * Bw + Ay * Bz - Az * By + Aw * Bx
|
||||||
Q.y = - Ax * Bz + Ay * Bw + Az * Bx + Aw * By
|
Q.y = - Ax * Bz + Ay * Bw + Az * Bx + Aw * By
|
||||||
Q.z = + Ax * By - Ay * Bx + Az * Bw + Aw * Bz
|
Q.z = + Ax * By - Ay * Bx + Az * Bw + Aw * Bz
|
||||||
Q.w = - Ax * Bx - Ay * By - Az * Bz + Aw * Bw
|
|
||||||
return Q
|
return Q
|
||||||
except: pass
|
except: pass
|
||||||
try: # vector (perform active rotation, i.e. q*v*q.conjugated)
|
try: # vector (perform active rotation, i.e. q*v*q.conjugated)
|
||||||
|
@ -309,10 +309,7 @@ class Quaternion:
|
||||||
|
|
||||||
angle = math.atan2(y,x)
|
angle = math.atan2(y,x)
|
||||||
|
|
||||||
if angle < 1e-3:
|
return angle, np.array([1.0, 0.0, 0.0] if angle < 1e-3 else [self.x / s, self.y / s, self.z / s])
|
||||||
return angle, np.array([1.0, 0.0, 0.0])
|
|
||||||
else:
|
|
||||||
return angle, np.array([self.x / s, self.y / s, self.z / s])
|
|
||||||
|
|
||||||
def asRodrigues(self):
|
def asRodrigues(self):
|
||||||
if self.w != 0.0:
|
if self.w != 0.0:
|
||||||
|
@ -320,7 +317,7 @@ class Quaternion:
|
||||||
else:
|
else:
|
||||||
return np.array([float('inf')]*3)
|
return np.array([float('inf')]*3)
|
||||||
|
|
||||||
def asEulers(self,type='bunge'):
|
def asEulers(self,type='bunge',degrees=False):
|
||||||
'''
|
'''
|
||||||
conversion taken from:
|
conversion taken from:
|
||||||
Melcher, A.; Unser, A.; Reichhardt, M.; Nestler, B.; Pötschke, M.; Selzer, M.
|
Melcher, A.; Unser, A.; Reichhardt, M.; Nestler, B.; Pötschke, M.; Selzer, M.
|
||||||
|
@ -362,7 +359,7 @@ class Quaternion:
|
||||||
# if angles[2] < 0.0:
|
# if angles[2] < 0.0:
|
||||||
# angles[2] += 2*math.pi
|
# angles[2] += 2*math.pi
|
||||||
|
|
||||||
return angles
|
return np.degrees(angles) if degrees else angles
|
||||||
|
|
||||||
|
|
||||||
# # Static constructors
|
# # Static constructors
|
||||||
|
@ -407,12 +404,15 @@ class Quaternion:
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def fromEulers(cls, eulers, type = 'Bunge'):
|
def fromEulers(cls, eulers, type = 'Bunge'):
|
||||||
c1 = math.cos(eulers[0] / 2.0)
|
|
||||||
s1 = math.sin(eulers[0] / 2.0)
|
eulers *= 0.5 # reduce to half angles
|
||||||
c2 = math.cos(eulers[1] / 2.0)
|
|
||||||
s2 = math.sin(eulers[1] / 2.0)
|
c1 = math.cos(eulers[0])
|
||||||
c3 = math.cos(eulers[2] / 2.0)
|
s1 = math.sin(eulers[0])
|
||||||
s3 = math.sin(eulers[2] / 2.0)
|
c2 = math.cos(eulers[1])
|
||||||
|
s2 = math.sin(eulers[1])
|
||||||
|
c3 = math.cos(eulers[2])
|
||||||
|
s3 = math.sin(eulers[2])
|
||||||
|
|
||||||
if type.lower() == 'bunge' or type.lower() == 'zxz':
|
if type.lower() == 'bunge' or type.lower() == 'zxz':
|
||||||
w = c1 * c2 * c3 - s1 * c2 * s3
|
w = c1 * c2 * c3 - s1 * c2 * s3
|
||||||
|
@ -797,10 +797,19 @@ class Orientation:
|
||||||
def asMatrix(self):
|
def asMatrix(self):
|
||||||
return self.quaternion.asMatrix()
|
return self.quaternion.asMatrix()
|
||||||
|
|
||||||
|
def inFZ(self):
|
||||||
|
return self.symmetry.inFZ(self.quaternion.asRodrigues())
|
||||||
|
|
||||||
|
def equivalentQuaternions(self):
|
||||||
|
return self.symmetry.equivalentQuaternions(self.quaternion)
|
||||||
|
|
||||||
|
def equivalentOrientations(self):
|
||||||
|
return map(lambda q: Orientation(quaternion=q,symmetry=self.symmetry.lattice),
|
||||||
|
self.equivalentQuaternions())
|
||||||
|
|
||||||
def reduced(self):
|
def reduced(self):
|
||||||
'''
|
'''
|
||||||
Transform orientation to fall into fundamental zone according to own (or given) symmetry
|
Transform orientation to fall into fundamental zone according to symmetry
|
||||||
'''
|
'''
|
||||||
|
|
||||||
for me in self.symmetry.equivalentQuaternions(self.quaternion):
|
for me in self.symmetry.equivalentQuaternions(self.quaternion):
|
||||||
|
@ -821,9 +830,7 @@ class Orientation:
|
||||||
for me in self.symmetry.equivalentQuaternions(self.quaternion):
|
for me in self.symmetry.equivalentQuaternions(self.quaternion):
|
||||||
me.conjugate()
|
me.conjugate()
|
||||||
for they in other.symmetry.equivalentQuaternions(other.quaternion):
|
for they in other.symmetry.equivalentQuaternions(other.quaternion):
|
||||||
# theQ = me * they
|
|
||||||
theQ = they * me
|
theQ = they * me
|
||||||
# if theQ.x < 0.0 or theQ.y < 0.0 or theQ.z < 0.0: theQ.conjugate() # speed up scanning since minimum angle is usually found for positive x,y,z
|
|
||||||
breaker = lowerSymmetry.inDisorientationSST(theQ.asRodrigues()) #\
|
breaker = lowerSymmetry.inDisorientationSST(theQ.asRodrigues()) #\
|
||||||
# or lowerSymmetry.inDisorientationSST(theQ.conjugated().asRodrigues())
|
# or lowerSymmetry.inDisorientationSST(theQ.conjugated().asRodrigues())
|
||||||
if breaker: break
|
if breaker: break
|
||||||
|
@ -838,9 +845,9 @@ class Orientation:
|
||||||
'''
|
'''
|
||||||
|
|
||||||
if SST: # pole requested to be within SST
|
if SST: # pole requested to be within SST
|
||||||
for i,q in enumerate(self.symmetry.equivalentQuaternions(self.quaternion)): # test all symmetric equivalent orientations
|
for i,q in enumerate(self.symmetry.equivalentQuaternions(self.quaternion)): # test all symmetric equivalent quaternions
|
||||||
pole = q.conjugated()*axis # align crystal direction to axis
|
pole = q.conjugated()*axis # align crystal direction to axis
|
||||||
if self.symmetry.inSST(pole): print i;break # found SST version
|
if self.symmetry.inSST(pole): break # found SST version
|
||||||
else:
|
else:
|
||||||
pole = self.quaternion.conjugated()*axis # align crystal direction to axis
|
pole = self.quaternion.conjugated()*axis # align crystal direction to axis
|
||||||
|
|
||||||
|
@ -876,11 +883,11 @@ class Orientation:
|
||||||
if not all(isinstance(item, Orientation) for item in orientationList):
|
if not all(isinstance(item, Orientation) for item in orientationList):
|
||||||
raise TypeError("Only instances of Orientation can be averaged.")
|
raise TypeError("Only instances of Orientation can be averaged.")
|
||||||
|
|
||||||
n = len(orientationList)
|
N = len(orientationList)
|
||||||
tmp_m = orientationList.pop(0).quaternion.asM()
|
M = orientationList.pop(0).quaternion.asM()
|
||||||
for tmp_o in orientationList:
|
for o in orientationList:
|
||||||
tmp_m += tmp_o.quaternion.asM()
|
M += o.quaternion.asM()
|
||||||
eig, vec = np.linalg.eig(tmp_m/n)
|
eig, vec = np.linalg.eig(M/N)
|
||||||
|
|
||||||
return Orientation(quaternion = Quaternion(quatArray = vec.T[eig.argmax()]))
|
return Orientation(quaternion = Quaternion(quatArray = vec.T[eig.argmax()]))
|
||||||
|
|
||||||
|
@ -1082,15 +1089,16 @@ class Orientation:
|
||||||
[[ 1, 0, 0],[ 1, 1, 0]]]),
|
[[ 1, 0, 0],[ 1, 1, 0]]]),
|
||||||
}
|
}
|
||||||
myPlane = planes [relationModel][variant,me]
|
myPlane = planes [relationModel][variant,me]
|
||||||
|
myPlane /= np.linalg.norm(myPlane)
|
||||||
myNormal = normals[relationModel][variant,me]
|
myNormal = normals[relationModel][variant,me]
|
||||||
myPlane = myPlane/np.linalg.norm(myPlane)
|
myNormal /= np.linalg.norm(myNormal)
|
||||||
myNormal = myNormal/np.linalg.norm(myNormal)
|
|
||||||
myMatrix = np.array([myPlane,myNormal,np.cross(myNormal,myPlane)])
|
myMatrix = np.array([myPlane,myNormal,np.cross(myNormal,myPlane)])
|
||||||
|
|
||||||
otherPlane = planes [relationModel][variant,other]
|
otherPlane = planes [relationModel][variant,other]
|
||||||
|
otherPlane /= np.linalg.norm(otherPlane)
|
||||||
otherNormal = normals[relationModel][variant,other]
|
otherNormal = normals[relationModel][variant,other]
|
||||||
otherPlane = otherPlane/np.linalg.norm(otherPlane)
|
otherNormal /= np.linalg.norm(otherNormal)
|
||||||
otherNormal = otherNormal/np.linalg.norm(otherNormal)
|
|
||||||
otherMatrix = np.array([otherPlane,otherNormal,np.cross(otherNormal,otherPlane)])
|
otherMatrix = np.array([otherPlane,otherNormal,np.cross(otherNormal,otherPlane)])
|
||||||
myMatrix = np.dot(self.asMatrix(),myMatrix)
|
myMatrix = np.dot(self.asMatrix(),myMatrix)
|
||||||
return Orientation(matrix=np.dot(otherMatrix.T,myMatrix))
|
|
||||||
|
return Orientation(matrix=np.dot(otherMatrix.T,myMatrix)) # no symmetry information ??
|
||||||
|
|
Loading…
Reference in New Issue