Package pytilities :: Package geometry :: Module vector
[hide private]
[frames] | no frames]

Source Code for Module pytilities.geometry.vector

  1  # Copyright (C) 2010 Tim Diels <limyreth@users.sourceforge.net> 
  2  #  
  3  # This file is part of pytilities. 
  4  #  
  5  # pytilities is free software: you can redistribute it and/or modify 
  6  # it under the terms of the GNU General Public License as published by 
  7  # the Free Software Foundation, either version 3 of the License, or 
  8  # (at your option) any later version. 
  9  #  
 10  # pytilities is distributed in the hope that it will be useful, 
 11  # but WITHOUT ANY WARRANTY; without even the implied warranty of 
 12  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 13  # GNU General Public License for more details. 
 14  #  
 15  # You should have received a copy of the GNU General Public License 
 16  # along with pytilities.  If not, see <http://www.gnu.org/licenses/>. 
 17  # 
 18   
 19  __docformat__ = 'reStructuredText' 
 20   
 21  import operator 
 22  import math 
 23   
 24  from pytilities.types import NumberType 
 25  from pytilities.delegation import delegated, delegator_factory 
 26  from pytilities.overloading import overloaded, Overload, Param 
27 28 29 -class _Storage(object):
30 - def __init__(self, x, y):
31 # Can't be a string as we must avoid nastiness. Consider for example 32 # what would happen when you add vectors ('1', 0) and ('0', 0), results 33 # in ('10', 0) 34 assert not isinstance(x, basestring) 35 assert not isinstance(y, basestring) 36 self.x = x 37 self.y = y
38
39 40 # NOTE: be careful with the awesome ways python int vs float math works 41 # As with statically typed languages, even here 5 / 2 == 2. As values do still 42 # have types. So, try not to mix ints and floats. 43 # If you'd rather not bother casting and keeping that sort of stuff in your 44 # mind, I recommend adding another Vector that wraps round this one and casts 45 # or asserts (if you are a bit of a performance freak ^^) all input. 46 @delegator_factory() 47 -class Vector(object):
48 49 """ 50 2D Point/Vector 51 52 Class fields: 53 54 - `INFINITY`: immutable Vector of positive infinity 55 - `NULL`: immutable Vector (0, 0) 56 57 Instance methods: 58 59 - `move_to`: Set x, y position 60 - `move_by`: Move vector by x, y 61 - `assign`: Assign values of other vector to this one 62 - `copy`: Shallow copy 63 - `normalize`: Normalize vector 64 - `normalized`: Get a normalized copy of this vector 65 - `dot`: Dot product 66 - `cross`: 67 - `reflect`: 68 69 Instance properties: 70 71 - `x`: Read-write, x position 72 - `y`: Read-write, y position 73 - `length`: Read-write, length of vector 74 - `length_squared`: Read-only, length squared 75 76 Operators: 77 78 str(s) 79 s == v 80 s != v 81 s + v 82 s += v 83 s - v 84 s -= v 85 s * n 86 s *= n 87 s / n 88 s /= n 89 -s 90 self explanatory 91 92 len(s) 93 returns 2 94 95 iter(s) 96 iterates over its x and y value 97 98 abs(s) 99 returns length of vector 100 """ 101 102 @staticmethod
103 - def __init_delegation_profiles(profiles):
104 profiles['default'] |= profiles['immutable']
105
106 - def __init_xy(self, x, y):
107 self.__storage = _Storage(x, y)
108
109 - def __init_storage(self, storage):
110 self.__storage = storage
111 112 @overloaded(( 113 Overload(__init_xy, 114 Param("x", NumberType, default=0), 115 Param("y", NumberType, default=0)), 116 Overload(__init_storage, 117 Param("storage"))))
118 - def __init__(self):
119 """ 120 Constructs a 2D Vector 121 122 Overloaded, parameters: 123 124 :a: 125 x :: float | int = 0 126 y :: float | int = 0 127 128 :b: 129 storage :: object(x, y) 130 an object that provides storage for the x and y values 131 """ 132 pass
133 134 @delegated() 135 @delegated("immutable", modifiers="r") 136 @property
137 - def x(self):
138 """Read-write, x position ::float | int""" 139 return self.__storage.x
140 141 @x.setter
142 - def x(self, value):
143 self.__storage.x = value
144 145 @delegated() 146 @delegated("immutable", modifiers="r") 147 @property
148 - def y(self):
149 """Read-write, y position ::float | int""" 150 return self.__storage.y
151 152 @y.setter
153 - def y(self, value):
154 self.__storage.y = value
155 156 @delegated()
157 - def move_to(self, x, y):
158 """ 159 Set x, y coords 160 161 Parameters: 162 163 `x` :: float | int 164 x position 165 166 `y` :: float | int 167 y position 168 """ 169 self.x, self.y = x, y
170 171 @delegated()
172 - def move_by(self, x, y):
173 """ 174 Move vector by (x, y) 175 176 Parameters: 177 178 `x` :: float | int 179 x position 180 181 `y` :: float | int 182 y position 183 """ 184 self.x += x 185 self.y += y
186 187 @delegated()
188 - def assign(self, v):
189 """ 190 Assigns the values of another vector to this vector 191 192 v :: Vector -- the other vector 193 """ 194 self.x = v.x 195 self.y = v.y
196 197 @delegated("immutable")
198 - def __str__( self ):
199 return "<%s (%s,%s)>" % (self.__class__.__name__, 200 self.x, self.y)
201 202 @delegated("immutable")
203 - def __copy__(self):
204 return Vector(self.x, self.y)
205 206 @delegated("immutable")
207 - def copy(self):
208 """Returns shallow copy :: Vector""" 209 return self.__copy__()
210 211 @delegated("immutable")
212 - def __eq__(self, other):
213 """other :: Vector""" 214 return self.x == other.x and \ 215 self.y == other.y
216 217 @delegated("immutable")
218 - def __neq__(self, other):
219 """other :: Vector""" 220 return not self.__eq__(other)
221 222 @delegated("immutable")
223 - def __len__(self):
224 return 2
225 226 @delegated("immutable")
227 - def __getitem__(self, key):
228 return (self.x, self.y)[key]
229 230 @delegated()
231 - def __setitem__(self, key, value):
232 (self.x, self.y)[key] = value
233 234 @delegated("immutable")
235 - def __iter__(self):
236 """Iterates over its x and y value""" 237 return iter((self.x, self.y))
238 239 @delegated("immutable")
240 - def __getattr__(self, name):
241 try: 242 return tuple([(self.x, self.y)['xy'.index(c)] \ 243 for c in name]) 244 except ValueError: 245 raise AttributeError, name
246 247 @delegated("immutable")
248 - def __add__(self, other):
249 """other :: Vector""" 250 v = self.copy() 251 v += other 252 return v
253 254 @delegated()
255 - def __iadd__(self, other):
256 """other :: Vector""" 257 self.x += other.x 258 self.y += other.y 259 return self
260 261 @delegated("immutable")
262 - def __sub__(self, other):
263 return self + (-other)
264 265 @delegated("immutable")
266 - def __mul__(self, other):
267 """other :: number""" 268 v = self.copy() 269 v *= other 270 return v
271 272 @delegated("immutable")
273 - def __rmul__(self, other):
274 """other :: number""" 275 return self * other
276 277 @delegated()
278 - def __imul__(self, other):
279 """other :: number""" 280 self.x *= other 281 self.y *= other 282 return self
283 284 @delegated("immutable")
285 - def __div__(self, other):
286 """other :: number""" 287 return Vector(operator.div(self.x, other), 288 operator.div(self.y, other))
289 290 @delegated("immutable")
291 - def __rdiv__(self, other):
292 """other :: number""" 293 return Vector(operator.div(other, self.x), 294 operator.div(other, self.y))
295 296 @delegated("immutable")
297 - def __floordiv__(self, other):
298 """other :: number""" 299 return Vector(operator.floordiv(self.x, other), 300 operator.floordiv(self.y, other))
301 302 @delegated("immutable")
303 - def __rfloordiv__(self, other):
304 """other :: number""" 305 return Vector(operator.floordiv(other, self.x), 306 operator.floordiv(other, self.y))
307 308 @delegated("immutable")
309 - def __truediv__(self, other):
310 """other :: number""" 311 return Vector(operator.truediv(self.x, other), 312 operator.truediv(self.y, other))
313 314 315 @delegated("immutable")
316 - def __rtruediv__(self, other):
317 """other :: number""" 318 return Vector(operator.truediv(other, self.x), 319 operator.truediv(other, self.y))
320 321 @delegated("immutable")
322 - def __neg__(self):
323 return Vector(-self.x, -self.y)
324 325 @delegated("immutable")
326 - def __pos__(self):
327 return self.copy()
328 329 @delegated("immutable")
330 - def __abs__(self):
331 return math.sqrt(self.x ** 2 + \ 332 self.y ** 2)
333 334 @delegated() 335 @property
336 - def length(self):
337 """ 338 Read-write, length of vector 339 340 Returns :: float | int 341 342 Set parameters: 343 344 value :: number 345 new length 346 """ 347 return abs(self)
348 349 @length.setter
350 - def length(self, value):
351 d = self.length * value 352 self *= value
353 354 @delegated("immutable") 355 @property
356 - def length_squared(self):
357 """Read-only, length squared :: float | int""" 358 return self.x ** 2 + \ 359 self.y ** 2
360 361 @delegated()
362 - def normalize(self):
363 """ 364 Normalize vector 365 366 Returns :: Vector 367 """ 368 d = self.length 369 370 if d: 371 self /= d 372 373 return self
374 375 @delegated("immutable")
376 - def normalized(self):
377 """ 378 Get a normalized copy of this vector 379 380 Returns :: Vector 381 """ 382 v = self.copy() 383 return v.normalize()
384 385 @delegated("immutable")
386 - def dot(self, other):
387 """ 388 Get the dot product of this vector with `other` 389 390 Parameters: 391 392 `other` :: Vector 393 the other vector 394 395 Returns :: float | int 396 """ 397 return self.x * other.x + \ 398 self.y * other.y
399 400 @delegated("immutable")
401 - def cross(self):
402 # TODO: doc this ya lazy git 403 return Vector(self.y, -self.x)
404 405 @delegated("immutable")
406 - def reflect(self, normal):
407 # TODO: doc this ya lazy git 408 # assume normal is normalized 409 d = 2 * (self.x * normal.x + self.y * normal.y) 410 return Vector(self.x - d * normal.x, 411 self.y - d * normal.y)
412 413 414 415 from .immutablevector import ImmutableVector 416 417 Vector.INFINITY = ImmutableVector(Vector(float("inf"), float("inf"))) 418 Vector.NULL = ImmutableVector(Vector(0, 0)) # saves us some vector spawning 419