1
2
3
4
5
6 """Vector class, including rotation-related functions."""
7
8 import numpy
9
10
12 """
13 Return angles, axis pair that corresponds to rotation matrix m.
14 """
15
16
17 t=0.5*(numpy.trace(m)-1)
18 t=max(-1, t)
19 t=min(1, t)
20 angle=numpy.arccos(t)
21 if angle<1e-15:
22
23 return 0.0, Vector(1,0,0)
24 elif angle<numpy.pi:
25
26 x=m[2,1]-m[1,2]
27 y=m[0,2]-m[2,0]
28 z=m[1,0]-m[0,1]
29 axis=Vector(x,y,z)
30 axis.normalize()
31 return angle, axis
32 else:
33
34 m00=m[0,0]
35 m11=m[1,1]
36 m22=m[2,2]
37 if m00>m11 and m00>m22:
38 x=numpy.sqrt(m00-m11-m22+0.5)
39 y=m[0,1]/(2*x)
40 z=m[0,2]/(2*x)
41 elif m11>m00 and m11>m22:
42 y=numpy.sqrt(m11-m00-m22+0.5)
43 x=m[0,1]/(2*y)
44 z=m[1,2]/(2*y)
45 else:
46 z=numpy.sqrt(m22-m00-m11+0.5)
47 x=m[0,2]/(2*z)
48 y=m[1,2]/(2*z)
49 axis=Vector(x,y,z)
50 axis.normalize()
51 return numpy.pi, axis
52
53
55 """
56 Returns the vector between a point and
57 the closest point on a line (ie. the perpendicular
58 projection of the point on the line).
59
60 @type line: L{Vector}
61 @param line: vector defining a line
62
63 @type point: L{Vector}
64 @param point: vector defining the point
65 """
66 line=line.normalized()
67 np=point.norm()
68 angle=line.angle(point)
69 return point-line**(np*numpy.cos(angle))
70
71
73 """
74 Calculate a left multiplying rotation matrix that rotates
75 theta rad around vector.
76
77 Example:
78
79 >>> m=rotaxis(pi, Vector(1,0,0))
80 >>> rotated_vector=any_vector.left_multiply(m)
81
82 @type theta: float
83 @param theta: the rotation angle
84
85
86 @type vector: L{Vector}
87 @param vector: the rotation axis
88
89 @return: The rotation matrix, a 3x3 Numeric array.
90 """
91 vector=vector.copy()
92 vector.normalize()
93 c=numpy.cos(theta)
94 s=numpy.sin(theta)
95 t=1-c
96 x,y,z=vector.get_array()
97 rot=numpy.zeros((3,3))
98
99 rot[0,0]=t*x*x+c
100 rot[0,1]=t*x*y-s*z
101 rot[0,2]=t*x*z+s*y
102
103 rot[1,0]=t*x*y+s*z
104 rot[1,1]=t*y*y+c
105 rot[1,2]=t*y*z-s*x
106
107 rot[2,0]=t*x*z-s*y
108 rot[2,1]=t*y*z+s*x
109 rot[2,2]=t*z*z+c
110 return rot
111
112 rotaxis=rotaxis2m
113
114
116 """
117 Return a (left multiplying) matrix that mirrors p onto q.
118
119 Example:
120 >>> mirror=refmat(p,q)
121 >>> qq=p.left_multiply(mirror)
122 >>> print q, qq # q and qq should be the same
123
124 @type p,q: L{Vector}
125 @return: The mirror operation, a 3x3 Numeric array.
126 """
127 p.normalize()
128 q.normalize()
129 if (p-q).norm()<1e-5:
130 return numpy.identity(3)
131 pq=p-q
132 pq.normalize()
133 b=pq.get_array()
134 b.shape=(3, 1)
135 i=numpy.identity(3)
136 ref=i-2*numpy.dot(b, numpy.transpose(b))
137 return ref
138
139
141 """
142 Return a (left multiplying) matrix that rotates p onto q.
143
144 Example:
145 >>> r=rotmat(p,q)
146 >>> print q, p.left_multiply(r)
147
148 @param p: moving vector
149 @type p: L{Vector}
150
151 @param q: fixed vector
152 @type q: L{Vector}
153
154 @return: rotation matrix that rotates p onto q
155 @rtype: 3x3 Numeric array
156 """
157 rot=numpy.dot(refmat(q, -p), refmat(p, -p))
158 return rot
159
160
162 """
163 Calculate the angle between 3 vectors
164 representing 3 connected points.
165
166 @param v1, v2, v3: the tree points that define the angle
167 @type v1, v2, v3: L{Vector}
168
169 @return: angle
170 @rtype: float
171 """
172 v1=v1-v2
173 v3=v3-v2
174 return v1.angle(v3)
175
176
178 """
179 Calculate the dihedral angle between 4 vectors
180 representing 4 connected points. The angle is in
181 ]-pi, pi].
182
183 @param v1, v2, v3, v4: the four points that define the dihedral angle
184 @type v1, v2, v3, v4: L{Vector}
185 """
186 ab=v1-v2
187 cb=v3-v2
188 db=v4-v3
189 u=ab**cb
190 v=db**cb
191 w=u**v
192 angle=u.angle(v)
193
194 try:
195 if cb.angle(w)>0.001:
196 angle=-angle
197 except ZeroDivisionError:
198
199 pass
200 return angle
201
202
204 "3D vector"
205
207 if y is None and z is None:
208
209 if len(x)!=3:
210 raise ValueError("Vector: x is not a "
211 "list/tuple/array of 3 numbers")
212 self._ar=numpy.array(x, 'd')
213 else:
214
215 self._ar=numpy.array((x, y, z), 'd')
216
218 x,y,z=self._ar
219 return "<Vector %.2f, %.2f, %.2f>" % (x,y,z)
220
222 "Return Vector(-x, -y, -z)"
223 a=-self._ar
224 return Vector(a)
225
227 "Return Vector+other Vector or scalar"
228 if isinstance(other, Vector):
229 a=self._ar+other._ar
230 else:
231 a=self._ar+numpy.array(other)
232 return Vector(a)
233
235 "Return Vector-other Vector or scalar"
236 if isinstance(other, Vector):
237 a=self._ar-other._ar
238 else:
239 a=self._ar-numpy.array(other)
240 return Vector(a)
241
243 "Return Vector.Vector (dot product)"
244 return sum(self._ar*other._ar)
245
247 "Return Vector(coords/a)"
248 a=self._ar/numpy.array(x)
249 return Vector(a)
250
252 "Return VectorxVector (cross product) or Vectorxscalar"
253 if isinstance(other, Vector):
254 a,b,c=self._ar
255 d,e,f=other._ar
256 c1=numpy.linalg.det(numpy.array(((b,c), (e,f))))
257 c2=-numpy.linalg.det(numpy.array(((a,c), (d,f))))
258 c3=numpy.linalg.det(numpy.array(((a,b), (d,e))))
259 return Vector(c1,c2,c3)
260 else:
261 a=self._ar*numpy.array(other)
262 return Vector(a)
263
266
269
271 return (i in self._ar)
272
274 "Return vector norm"
275 return numpy.sqrt(sum(self._ar*self._ar))
276
278 "Return square of vector norm"
279 return abs(sum(self._ar*self._ar))
280
282 "Normalize the Vector"
283 self._ar=self._ar/self.norm()
284
286 "Return a normalized copy of the Vector"
287 v=self.copy()
288 v.normalize()
289 return v
290
292 "Return angle between two vectors"
293 n1=self.norm()
294 n2=other.norm()
295 c=(self*other)/(n1*n2)
296
297 c=min(c,1)
298 c=max(-1,c)
299 return numpy.arccos(c)
300
302 "Return (a copy of) the array of coordinates"
303 return numpy.array(self._ar)
304
306 "Return Vector=Matrix x Vector"
307 a=numpy.dot(matrix, self._ar)
308 return Vector(a)
309
311 "Return Vector=Vector x Matrix"
312 a=numpy.dot(self._ar, matrix)
313 return Vector(a)
314
316 "Return a deep copy of the Vector"
317 return Vector(self._ar)
318
319 if __name__=="__main__":
320
321 from numpy.random import random
322
323 v1=Vector(0,0,1)
324 v2=Vector(0,0,0)
325 v3=Vector(0,1,0)
326 v4=Vector(1,1,0)
327
328 v4.normalize()
329
330 print v4
331
332 print calc_angle(v1, v2, v3)
333 dih=calc_dihedral(v1, v2, v3, v4)
334
335 assert(dih>0)
336 print "DIHEDRAL ", dih
337
338 ref=refmat(v1, v3)
339 rot=rotmat(v1, v3)
340
341 print v3
342 print v1.left_multiply(ref)
343 print v1.left_multiply(rot)
344 print v1.right_multiply(numpy.transpose(rot))
345
346
347 print v1-v2
348 print v1-1
349 print v1+(1,2,3)
350
351 print v1+v2
352 print v1+3
353 print v1-(1,2,3)
354
355 print v1*v2
356
357 print v1/2
358 print v1/(1,2,3)
359
360 print v1**v2
361 print v1**2
362 print v1**(1,2,3)
363
364 print v1.norm()
365
366 print v1.normsq()
367
368 v1[2]=10
369 print v1
370
371 print v1[2]
372
373 print numpy.array(v1)
374
375 print "ROT"
376
377 angle=random()*numpy.pi
378 axis=Vector(random(3)-random(3))
379 axis.normalize()
380
381 m=rotaxis(angle, axis)
382
383 cangle, caxis=m2rotaxis(m)
384
385 print angle-cangle
386 print axis-caxis
387 print
388