1 """ This file contains the following functions,
2 rotax
3 mat_to_quat
4 mat_to_axis_angle
5 inverse4X4
6 rotVectToVect
7 interpolate3DTransform
8 """
9
10
11 from math import pi, sin, cos, sqrt
12 import Numeric as N
13 degtorad = pi/180.
14
15
16 -def rotax( a, b, tau, transpose=1 ):
17 """
18 Build 4x4 matrix of clockwise rotation about axis a-->b
19 by angle tau (radians).
20 a and b are sequences of 3 floats each
21 Result is a homogenous 4x4 transformation matrix.
22 NOTE: This has been changed by Brian, 8/30/01: rotax now returns
23 the rotation matrix, _not_ the transpose. This is to get
24 consistency across rotax, mat_to_quat and the classes in
25 transformation.py
26 when transpose is 1 (default) a C-style rotation matrix is returned
27 i.e. to be used is the following way Mx (opposite of OpenGL style which
28 is using the FORTRAN style)
29 """
30
31 assert len(a) == 3
32 assert len(b) == 3
33 if tau <= -2*pi or tau >= 2*pi:
34 tau = tau%(2*pi)
35
36 ct = cos(tau)
37 ct1 = 1.0 - ct
38 st = sin(tau)
39
40
41
42
43 v = [b[0]-a[0], b[1]-a[1], b[2]-a[2]]
44 s = v[0]*v[0]+v[1]*v[1]+v[2]*v[2]
45 if s > 0.0:
46 s = sqrt(s)
47 v = [v[0]/s, v[1]/s, v[2]/s]
48 else:
49 val = sqrt(1.0/3.0)
50 v = (val, val, val)
51
52 rot = N.zeros( (4,4), 'f' )
53
54
55 v2 = [v[0]*v[0], v[1]*v[1], v[2]*v[2]]
56 v3 = [(1.0-v2[0])*ct, (1.0-v2[1])*ct, (1.0-v2[2])*ct]
57 rot[0][0]=v2[0]+v3[0]
58 rot[1][1]=v2[1]+v3[1]
59 rot[2][2]=v2[2]+v3[2]
60 rot[3][3] = 1.0;
61
62 v2 = [v[0]*st, v[1]*st, v[2]*st]
63 rot[1][0]=v[0]*v[1] * ct1-v2[2]
64 rot[2][1]=v[1]*v[2] * ct1-v2[0]
65 rot[0][2]=v[2]*v[0] * ct1-v2[1]
66 rot[0][1]=v[0]*v[1] * ct1+v2[2]
67 rot[1][2]=v[1]*v[2] * ct1+v2[0]
68 rot[2][0]=v[2]*v[0] * ct1+v2[1]
69
70
71 for i in (0,1,2):
72 rot[3][i] = a[i]
73 for j in (0,1,2):
74 rot[3][i] = rot[3][i]-rot[j][i]*a[j]
75 rot[i][3]=0.0
76
77 if transpose:
78 return rot
79 else:
80 return N.transpose(rot)
81
82
83 from math import asin
84
86 """returns a 4x4 transformation that will align vect1 with vect2
87 vect1 and vect2 can be any vector (non-normalized)
88 """
89 v1x, v1y, v1z = vect1
90 v2x, v2y, v2z = vect2
91
92
93 norm = 1.0/sqrt(v1x*v1x + v1y*v1y + v1z*v1z )
94 v1x *= norm
95 v1y *= norm
96 v1z *= norm
97 norm = 1.0/sqrt(v2x*v2x + v2y*v2y + v2z*v2z )
98 v2x *= norm
99 v2y *= norm
100 v2z *= norm
101
102
103 cx = v1y*v2z - v1z*v2y
104 cy = v1z*v2x - v1x*v2z
105 cz = v1x*v2y - v1y*v2x
106
107
108 nc = sqrt(cx*cx + cy*cy + cz*cz)
109 if nc==0.0:
110 return [ [1., 0., 0., 0.],
111 [0., 1., 0., 0.],
112 [0., 0., 1., 0.],
113 [0., 0., 0., 1.] ]
114
115 cx /= nc
116 cy /= nc
117 cz /= nc
118
119
120 if nc<0.0:
121 if i is not None:
122 print 'truncating nc on step:', i, nc
123 nc=0.0
124 elif nc>1.0:
125 if i is not None:
126 print 'truncating nc on step:', i, nc
127 nc=1.0
128
129 alpha = asin(nc)
130 if (v1x*v2x + v1y*v2y + v1z*v2z) < 0.0:
131 alpha = pi - alpha
132
133
134
135
136 ct = cos(alpha)
137 ct1 = 1.0 - ct
138 st = sin(alpha)
139
140 rot = [ [0., 0., 0., 0.],
141 [0., 0., 0., 0.],
142 [0., 0., 0., 0.],
143 [0., 0., 0., 0.] ]
144
145
146 rv2x, rv2y, rv2z = cx*cx, cy*cy, cz*cz
147 rv3x, rv3y, rv3z = (1.0-rv2x)*ct, (1.0-rv2y)*ct, (1.0-rv2z)*ct
148 rot[0][0] = rv2x + rv3x
149 rot[1][1] = rv2y + rv3y
150 rot[2][2] = rv2z + rv3z
151 rot[3][3] = 1.0;
152
153 rv4x, rv4y, rv4z = cx*st, cy*st, cz*st
154 rot[0][1] = cx * cy * ct1 - rv4z
155 rot[1][2] = cy * cz * ct1 - rv4x
156 rot[2][0] = cz * cx * ct1 - rv4y
157 rot[1][0] = cx * cy * ct1 + rv4z
158 rot[2][1] = cy * cz * ct1 + rv4x
159 rot[0][2] = cz * cx * ct1 + rv4y
160
161 return rot
162
163
165 """ takes a four by four matrix (optionally with shape (16,) and
166 converts it into the axis of rotation and angle to rotate by
167 (x,y,z,theta). It does not expect an OpenGL style, transposed
168 matrix, so is consistent with rotax
169 """
170
171 if N.shape(matrix) not in ((16,),(4,4)):
172 raise ValueError("Argument must Numeric array of shape (4,4) or (16,)")
173
174 if N.shape(matrix) == (4, 4):
175 matrix = N.reshape(matrix,(16,))
176
177 cofactor1 = matrix[5]*matrix[10] - matrix[9]*matrix[6]
178 cofactor2 = matrix[8]*matrix[6] - matrix[4]*matrix[10]
179 cofactor3 = matrix[4]*matrix[9] - matrix[8]*matrix[5]
180
181 det = matrix[0]*cofactor1 + matrix[1]*cofactor2 + matrix[2]*cofactor3
182 if not (0.999 < det < 1.001):
183 print "Not a unit matrix: so not a pure rotation"
184 print 'Value of Determinant is: ',det
185 trace = matrix[0] + matrix[5] + matrix[10] + matrix[15]
186 if trace > 0.0000001:
187 S = 0.5/sqrt(trace)
188 Qw = 0.25/S
189 Qx = (matrix[9]-matrix[6])*S
190 Qy = (matrix[2]-matrix[8])*S
191 Qz = (matrix[4]-matrix[1])*S
192 else:
193 Qw = 0.
194 diagonal = ((matrix[0],0),
195 (matrix[5],5),
196 (matrix[10],10))
197 idx = max(diagonal)[1]
198 if idx == 0:
199 S = sqrt(1.0 + matrix[0] - matrix[5] - matrix[10])*2
200 Qy = (matrix[1] + matrix[4] ) / S
201 Qz = (matrix[2] + matrix[8] ) / S
202 Qx = N.sqrt(1-Qy*Qy-Qz*Qz)
203 elif idx==5:
204 S = sqrt( 1.0 + matrix[5] - matrix[0] - matrix[10] )*2
205 Qx = (matrix[1] + matrix[4] ) / S
206 Qz = (matrix[6] + matrix[9] ) / S
207 Qy = N.sqrt(1-Qx*Qx-Qz*Qz)
208 elif idx==10:
209 S = sqrt( 1.0 + matrix[10] - matrix[0] - matrix[5] )*2
210 Qx = (matrix[2] + matrix[8] ) / S
211 Qy = (matrix[6] + matrix[9] ) / S
212 Qz = N.sqrt(1-Qx*Qx-Qy*Qy)
213
214 if Qw != 1.:
215 angle = N.arccos(Qw)
216 theta = angle*360./N.pi
217 Z = sqrt(Qx*Qx + Qy*Qy + Qz*Qz)
218 if transpose:
219 Qx = -Qx/Z
220 Qy = -Qy/Z
221 Qz = -Qz/Z
222 else:
223 Qx = Qx/Z
224 Qy = Qy/Z
225 Qz = Qz/Z
226 Qw = theta
227 return [Qx,Qy,Qz,Qw]
228 else:
229 return [0.,0.,0.,0.]
230
231
232
234 """ returns the inverse of the given 4x4 transformation matrix
235 t_1: the negetive of Translation vector
236 r_1: the inverse of rotation matrix
237
238 inversed transformation is
239 1) t_1 applied first
240 2) then r_1 is applied
241
242 to validate the result, N.matrixmultiply(matrix, mat_inverse)==N.identity(4,'f')
243 """
244
245 if matrix.shape !=(4,4) and matrix.shape !=(16,) :
246 raise ValueError("Argument must Numeric array of shape (4,4) or (16,)")
247 return None
248 if matrix.shape ==(16,):
249 matrix=N.array(matrix,'f')
250 matrix=N.reshape(matrix,(4,4))
251 t_1=N.identity(4,'f')
252 t_1[:2,3]= - matrix[:2, 3]
253 r_1=N.identity(4,'f')
254 r_1[:3,:3] = N.transpose(matrix[:3,:3])
255 mat_inverse=N.matrixmultiply(r_1, t_1)
256
257 return mat_inverse
258
259
261 """
262 NOTE: This function is added by Yong 2/01/04: given a 4x4 transformation
263 matrix of hinge motion, now returns the rotation angle and axis (defined by
264 vector and a point) Please be noticed that if the motion is not hinge, the
265 function will complain and return none
266 """
267 if matrix.shape != (16,) and matrix.shape != (4,4):
268 raise ValueError("matrix should be of shape (4,4) or (16,)")
269 return None
270
271 if matrix.shape == (16,):
272 matrix = N.reshape(matrix, (4,4))
273
274 from math import sin, cos, pi, sqrt, fabs, acos
275 degtorad = pi/180.
276
277 transf = matrix
278 from mglutil.math.rotax import mat_to_quat
279 rotMat = N.identity(4, 'f')
280 rotMat[:3,:3] = transf[:3,:3]
281 qB = mat_to_quat(matrix=N.array(rotMat).flat)
282 angle = qB[3]
283 sa=sin(angle*degtorad/2.0)
284 if(fabs(angle) < 0.0005):
285 sa = 1
286 if angle > 180.:
287 vector=[-qB[0]/sa, -qB[1]/sa, -qB[2]/sa]
288 else:
289 vector=[qB[0]/sa, qB[1]/sa, qB[2]/sa]
290 tranMat = transf[3,:3]
291
292
293 a=vector
294 b=tranMat
295 c =[a[0]-b[0], a[1]-b[1], a[2]-b[2]]
296 a2= a[0]*a[0] + a[1]*a[1] + a[2]*a[2]
297 b2= b[0]*b[0] + b[1]*b[1] + b[2]*b[2]
298 c2= c[0]*c[0] + c[1]*c[1] + c[2]*c[2]
299 theta = acos((c2-a2-b2)/(2* sqrt(a2*b2))) / pi * 180
300 if fabs(theta -90) > 1e-4:
301 raise ValueError("The given transformation is not a hinge motion")
302 return None, None, None
303
304 ratio = sqrt( 1. / (2. * (1.- cos(degtorad * angle ))))
305 p = [tranMat[0]*ratio, tranMat[1]*ratio, tranMat[2]*ratio]
306
307 ang = 90. - angle/2.0
308 rot = rotax([0.,0.,0.], vector, ang*degtorad, transpose=1)
309 rot = rot[:3,:3]
310 point = N.matrixmultiply(p, rot)
311
312 return vector, point, angle
313
314
370
371
394
395
396
398 """ called only by interpolate3DTransform()
399 """
400 if mat.shape != (16,) and mat.shape != (4,4):
401 raise ValueError("matrix should be of shape (4,4) or (16,)")
402 return None
403
404 if mat.shape == (16,):
405 mat = N.reshape(mat, (4,4))
406
407
408
409
410
411 p = percent
412 transf = mat[:,:]
413 rotMat = N.identity(4, 'f')
414 rotMat[:3,:3]=transf.astype(N.Float32)[:3,:3]
415
416 from mglutil.math.rotax import mat_to_quat
417 quat = mat_to_quat(matrix=N.array(rotMat).flat)
418 angle = quat[3] * p
419
420 newRotMat = rotax([0.,0.,0.], quat[:3], angle*degtorad, transpose = 1)
421 newTranMat = N.identity(4, 'f')
422 newTranMat[3][0] = transf[3][0]*p
423 newTranMat[3][1] = transf[3][1]*p
424 newTranMat[3][2] = transf[3][2]*p
425
426 transform = newRotMat
427 transform[3][0] = newTranMat[3][0]
428 transform[3][1] = newTranMat[3][1]
429 transform[3][2] = newTranMat[3][2]
430
431
432 return transform
433