Module vec2d
[hide private]
[frames] | no frames]

Source Code for Module vec2d

  1  ################################################################################## 
  2  # Copyright (c) 2011, Daniel Urieli, Peter Stone 
  3  # University of Texas at Austin 
  4  # All right reserved 
  5  #  
  6  # Based On: 
  7  #  
  8  # Copyright (c) 2000-2003, Jelle Kok, University of Amsterdam 
  9  # All rights reserved. 
 10  #  
 11  # Redistribution and use in source and binary forms, with or without 
 12  # modification, are permitted provided that the following conditions are met: 
 13  #  
 14  # 1. Redistributions of source code must retain the above copyright notice, this 
 15  # list of conditions and the following disclaimer. 
 16  #  
 17  # 2. Redistributions in binary form must reproduce the above copyright notice, 
 18  # this list of conditions and the following disclaimer in the documentation 
 19  # and/or other materials provided with the distribution. 
 20  #  
 21  # 3. Neither the name of the University of Amsterdam nor the names of its 
 22  # contributors may be used to endorse or promote products derived from this 
 23  # software without specific prior written permission. 
 24  #  
 25  # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
 26  # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
 27  # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
 28  # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE 
 29  # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 
 30  # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
 31  # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
 32  # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 
 33  # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
 34  # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
 35  ################################################################################## 
 36   
 37   
 38   
 39   
 40  ################################################################################ 
 41  ################################################################################ 
 42  # The whole file is taken from http://www.pygame.org/wiki/2DVectorClass  
 43  ################################################################################ 
 44  ################################################################################ 
 45   
 46  import operator 
 47  import math 
 48    
49 -class Vec2d(object):
50 """2d vector class, supports vector and scalar operators, 51 and also provides a bunch of high level functions 52 """ 53 __slots__ = ['x', 'y'] 54
55 - def __init__(self, x_or_pair, y = None):
56 if y == None: 57 self.x = x_or_pair[0] 58 self.y = x_or_pair[1] 59 else: 60 self.x = x_or_pair 61 self.y = y
62
63 - def __len__(self):
64 return 2
65
66 - def __getitem__(self, key):
67 if key == 0: 68 return self.x 69 elif key == 1: 70 return self.y 71 else: 72 raise IndexError("Invalid subscript "+str(key)+" to Vec2d")
73
74 - def __setitem__(self, key, value):
75 if key == 0: 76 self.x = value 77 elif key == 1: 78 self.y = value 79 else: 80 raise IndexError("Invalid subscript "+str(key)+" to Vec2d")
81 82 # String representaion (for debugging)
83 - def __repr__(self):
84 return 'Vec2d(%s, %s)' % (self.x, self.y)
85 86 # Comparison
87 - def __eq__(self, other):
88 if hasattr(other, "__getitem__") and len(other) == 2: 89 return self.x == other[0] and self.y == other[1] 90 else: 91 return False
92
93 - def __ne__(self, other):
94 if hasattr(other, "__getitem__") and len(other) == 2: 95 return self.x != other[0] or self.y != other[1] 96 else: 97 return True
98
99 - def __nonzero__(self):
100 return bool(self.x or self.y)
101 102 # Generic operator handlers
103 - def _o2(self, other, f):
104 "Any two-operator operation where the left operand is a Vec2d" 105 if isinstance(other, Vec2d): 106 return Vec2d(f(self.x, other.x), 107 f(self.y, other.y)) 108 elif (hasattr(other, "__getitem__")): 109 return Vec2d(f(self.x, other[0]), 110 f(self.y, other[1])) 111 else: 112 return Vec2d(f(self.x, other), 113 f(self.y, other))
114
115 - def _r_o2(self, other, f):
116 "Any two-operator operation where the right operand is a Vec2d" 117 if (hasattr(other, "__getitem__")): 118 return Vec2d(f(other[0], self.x), 119 f(other[1], self.y)) 120 else: 121 return Vec2d(f(other, self.x), 122 f(other, self.y))
123
124 - def _io(self, other, f):
125 "inplace operator" 126 if (hasattr(other, "__getitem__")): 127 self.x = f(self.x, other[0]) 128 self.y = f(self.y, other[1]) 129 else: 130 self.x = f(self.x, other) 131 self.y = f(self.y, other) 132 return self
133 134 # Addition
135 - def __add__(self, other):
136 if isinstance(other, Vec2d): 137 return Vec2d(self.x + other.x, self.y + other.y) 138 elif hasattr(other, "__getitem__"): 139 return Vec2d(self.x + other[0], self.y + other[1]) 140 else: 141 return Vec2d(self.x + other, self.y + other)
142 __radd__ = __add__ 143
144 - def __iadd__(self, other):
145 if isinstance(other, Vec2d): 146 self.x += other.x 147 self.y += other.y 148 elif hasattr(other, "__getitem__"): 149 self.x += other[0] 150 self.y += other[1] 151 else: 152 self.x += other 153 self.y += other 154 return self
155 156 # Subtraction
157 - def __sub__(self, other):
158 if isinstance(other, Vec2d): 159 return Vec2d(self.x - other.x, self.y - other.y) 160 elif (hasattr(other, "__getitem__")): 161 return Vec2d(self.x - other[0], self.y - other[1]) 162 else: 163 return Vec2d(self.x - other, self.y - other)
164 - def __rsub__(self, other):
165 if isinstance(other, Vec2d): 166 return Vec2d(other.x - self.x, other.y - self.y) 167 if (hasattr(other, "__getitem__")): 168 return Vec2d(other[0] - self.x, other[1] - self.y) 169 else: 170 return Vec2d(other - self.x, other - self.y)
171 - def __isub__(self, other):
172 if isinstance(other, Vec2d): 173 self.x -= other.x 174 self.y -= other.y 175 elif (hasattr(other, "__getitem__")): 176 self.x -= other[0] 177 self.y -= other[1] 178 else: 179 self.x -= other 180 self.y -= other 181 return self
182 183 # Multiplication
184 - def __mul__(self, other):
185 if isinstance(other, Vec2d): 186 return Vec2d(self.x*other.x, self.y*other.y) 187 if (hasattr(other, "__getitem__")): 188 return Vec2d(self.x*other[0], self.y*other[1]) 189 else: 190 return Vec2d(self.x*other, self.y*other)
191 __rmul__ = __mul__ 192
193 - def __imul__(self, other):
194 if isinstance(other, Vec2d): 195 self.x *= other.x 196 self.y *= other.y 197 elif (hasattr(other, "__getitem__")): 198 self.x *= other[0] 199 self.y *= other[1] 200 else: 201 self.x *= other 202 self.y *= other 203 return self
204 205 # Division
206 - def __div__(self, other):
207 return self._o2(other, operator.div)
208 - def __rdiv__(self, other):
209 return self._r_o2(other, operator.div)
210 - def __idiv__(self, other):
211 return self._io(other, operator.div)
212
213 - def __floordiv__(self, other):
214 return self._o2(other, operator.floordiv)
215 - def __rfloordiv__(self, other):
216 return self._r_o2(other, operator.floordiv)
217 - def __ifloordiv__(self, other):
218 return self._io(other, operator.floordiv)
219
220 - def __truediv__(self, other):
221 return self._o2(other, operator.truediv)
222 - def __rtruediv__(self, other):
223 return self._r_o2(other, operator.truediv)
224 - def __itruediv__(self, other):
225 return self._io(other, operator.floordiv)
226 227 # Modulo
228 - def __mod__(self, other):
229 return self._o2(other, operator.mod)
230 - def __rmod__(self, other):
231 return self._r_o2(other, operator.mod)
232
233 - def __divmod__(self, other):
234 return self._o2(other, operator.divmod)
235 - def __rdivmod__(self, other):
236 return self._r_o2(other, operator.divmod)
237 238 # Exponentation
239 - def __pow__(self, other):
240 return self._o2(other, operator.pow)
241 - def __rpow__(self, other):
242 return self._r_o2(other, operator.pow)
243 244 # Bitwise operators
245 - def __lshift__(self, other):
246 return self._o2(other, operator.lshift)
247 - def __rlshift__(self, other):
248 return self._r_o2(other, operator.lshift)
249
250 - def __rshift__(self, other):
251 return self._o2(other, operator.rshift)
252 - def __rrshift__(self, other):
253 return self._r_o2(other, operator.rshift)
254
255 - def __and__(self, other):
256 return self._o2(other, operator.and_)
257 __rand__ = __and__ 258
259 - def __or__(self, other):
260 return self._o2(other, operator.or_)
261 __ror__ = __or__ 262
263 - def __xor__(self, other):
264 return self._o2(other, operator.xor)
265 __rxor__ = __xor__ 266 267 # Unary operations
268 - def __neg__(self):
269 return Vec2d(operator.neg(self.x), operator.neg(self.y))
270
271 - def __pos__(self):
272 return Vec2d(operator.pos(self.x), operator.pos(self.y))
273
274 - def __abs__(self):
275 return Vec2d(abs(self.x), abs(self.y))
276
277 - def __invert__(self):
278 return Vec2d(-self.x, -self.y)
279 280 # vectory functions
281 - def get_length_sqrd(self):
282 return self.x**2 + self.y**2
283
284 - def get_length(self):
285 return math.sqrt(self.x**2 + self.y**2)
286 - def __setlength(self, value):
287 length = self.get_length() 288 self.x *= value/length 289 self.y *= value/length
290 length = property(get_length, __setlength, None, "gets or sets the magnitude of the vector") 291
292 - def rotate(self, angle_degrees):
293 radians = math.radians(angle_degrees) 294 cos = math.cos(radians) 295 sin = math.sin(radians) 296 x = self.x*cos - self.y*sin 297 y = self.x*sin + self.y*cos 298 self.x = x 299 self.y = y
300
301 - def rotated(self, angle_degrees):
302 radians = math.radians(angle_degrees) 303 cos = math.cos(radians) 304 sin = math.sin(radians) 305 x = self.x*cos - self.y*sin 306 y = self.x*sin + self.y*cos 307 return Vec2d(x, y)
308
309 - def get_angle(self):
310 if (self.get_length_sqrd() == 0): 311 return 0 312 return math.degrees(math.atan2(self.y, self.x))
313 - def __setangle(self, angle_degrees):
314 self.x = self.length 315 self.y = 0 316 self.rotate(angle_degrees)
317 angle = property(get_angle, __setangle, None, "gets or sets the angle of a vector") 318
319 - def get_angle_between(self, other):
320 cross = self.x*other[1] - self.y*other[0] 321 dot = self.x*other[0] + self.y*other[1] 322 return math.degrees(math.atan2(cross, dot))
323
324 - def normalized(self):
325 length = self.length 326 if length != 0: 327 return self/length 328 return Vec2d(self)
329
330 - def normalize_return_length(self):
331 length = self.length 332 if length != 0: 333 self.x /= length 334 self.y /= length 335 return length
336
337 - def perpendicular(self):
338 return Vec2d(-self.y, self.x)
339
340 - def perpendicular_normal(self):
341 length = self.length 342 if length != 0: 343 return Vec2d(-self.y/length, self.x/length) 344 return Vec2d(self)
345
346 - def dot(self, other):
347 return float(self.x*other[0] + self.y*other[1])
348
349 - def get_distance(self, other):
350 return math.sqrt((self.x - other[0])**2 + (self.y - other[1])**2)
351
352 - def get_dist_sqrd(self, other):
353 return (self.x - other[0])**2 + (self.y - other[1])**2
354
355 - def projection(self, other):
356 other_length_sqrd = other[0]*other[0] + other[1]*other[1] 357 projected_length_times_other_length = self.dot(other) 358 return other*(projected_length_times_other_length/other_length_sqrd)
359
360 - def cross(self, other):
361 return self.x*other[1] - self.y*other[0]
362
363 - def interpolate_to(self, other, range):
364 return Vec2d(self.x + (other[0] - self.x)*range, self.y + (other[1] - self.y)*range)
365
366 - def convert_to_basis(self, x_vector, y_vector):
367 return Vec2d(self.dot(x_vector)/x_vector.get_length_sqrd(), self.dot(y_vector)/y_vector.get_length_sqrd())
368
369 - def __getstate__(self):
370 return [self.x, self.y]
371
372 - def __setstate__(self, dict):
373 self.x, self.y = dict
374 375 ######################################################################## 376 ## Unit Testing ## 377 ######################################################################## 378 if __name__ == "__main__": 379 380 import unittest 381 import pickle 382 383 ####################################################################
384 - class UnitTestVec2D(unittest.TestCase):
385
386 - def setUp(self):
387 pass
388
389 - def testCreationAndAccess(self):
390 v = Vec2d(111,222) 391 self.assert_(v.x == 111 and v.y == 222) 392 v.x = 333 393 v[1] = 444 394 self.assert_(v[0] == 333 and v[1] == 444)
395
396 - def testMath(self):
397 v = Vec2d(111,222) 398 self.assertEqual(v + 1, Vec2d(112,223)) 399 self.assert_(v - 2 == [109,220]) 400 self.assert_(v * 3 == (333,666)) 401 self.assert_(v / 2.0 == Vec2d(55.5, 111)) 402 self.assert_(v / 2 == (55.5, 111)) 403 self.assert_(v ** Vec2d(2,3) == [12321, 10941048]) 404 self.assert_(v + [-11, 78] == Vec2d(100, 300)) 405 self.assert_(v / [10,2] == [11.1,111])
406
407 - def testReverseMath(self):
408 v = Vec2d(111,222) 409 self.assert_(1 + v == Vec2d(112,223)) 410 self.assert_(2 - v == [-109,-220]) 411 self.assert_(3 * v == (333,666)) 412 self.assert_([222,888] / v == [2,4]) 413 self.assert_([111,222] ** Vec2d(2,3) == [12321, 10941048]) 414 self.assert_([-11, 78] + v == Vec2d(100, 300))
415
416 - def testUnary(self):
417 v = Vec2d(111,222) 418 v = -v 419 self.assert_(v == [-111,-222]) 420 v = abs(v) 421 self.assert_(v == [111,222])
422
423 - def testLength(self):
424 v = Vec2d(3,4) 425 self.assert_(v.length == 5) 426 self.assert_(v.get_length_sqrd() == 25) 427 self.assert_(v.normalize_return_length() == 5) 428 self.assert_(v.length == 1) 429 v.length = 5 430 self.assert_(v == Vec2d(3,4)) 431 v2 = Vec2d(10, -2) 432 self.assert_(v.get_distance(v2) == (v - v2).get_length())
433
434 - def testAngles(self):
435 v = Vec2d(0, 3) 436 self.assertEquals(v.angle, 90) 437 v2 = Vec2d(v) 438 v.rotate(-90) 439 self.assertEqual(v.get_angle_between(v2), 90) 440 v2.angle -= 90 441 self.assertEqual(v.length, v2.length) 442 self.assertEquals(v2.angle, 0) 443 self.assertEqual(v2, [3, 0]) 444 self.assert_((v - v2).length < .00001) 445 self.assertEqual(v.length, v2.length) 446 v2.rotate(300) 447 self.assertAlmostEquals(v.get_angle_between(v2), -60) 448 v2.rotate(v2.get_angle_between(v)) 449 angle = v.get_angle_between(v2) 450 self.assertAlmostEquals(v.get_angle_between(v2), 0)
451
452 - def testHighLevel(self):
453 basis0 = Vec2d(5.0, 0) 454 basis1 = Vec2d(0, .5) 455 v = Vec2d(10, 1) 456 self.assert_(v.convert_to_basis(basis0, basis1) == [2, 2]) 457 self.assert_(v.projection(basis0) == (10, 0)) 458 self.assert_(basis0.dot(basis1) == 0)
459
460 - def testCross(self):
461 lhs = Vec2d(1, .5) 462 rhs = Vec2d(4,6) 463 self.assert_(lhs.cross(rhs) == 4)
464
465 - def testComparison(self):
466 int_vec = Vec2d(3, -2) 467 flt_vec = Vec2d(3.0, -2.0) 468 zero_vec = Vec2d(0, 0) 469 self.assert_(int_vec == flt_vec) 470 self.assert_(int_vec != zero_vec) 471 self.assert_((flt_vec == zero_vec) == False) 472 self.assert_((flt_vec != int_vec) == False) 473 self.assert_(int_vec == (3, -2)) 474 self.assert_(int_vec != [0, 0]) 475 self.assert_(int_vec != 5) 476 self.assert_(int_vec != [3, -2, -5])
477
478 - def testInplace(self):
479 inplace_vec = Vec2d(5, 13) 480 inplace_ref = inplace_vec 481 inplace_src = Vec2d(inplace_vec) 482 inplace_vec *= .5 483 inplace_vec += .5 484 inplace_vec /= (3, 6) 485 inplace_vec += Vec2d(-1, -1) 486 self.assertEquals(inplace_vec, inplace_ref)
487
488 - def testPickle(self):
489 testvec = Vec2d(5, .3) 490 testvec_str = pickle.dumps(testvec) 491 loaded_vec = pickle.loads(testvec_str) 492 self.assertEquals(testvec, loaded_vec)
493 494 #################################################################### 495 unittest.main() 496 497 ######################################################################## 498