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

Source Code for Module vec2d

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