Source code for pymunk.vec2d
# ----------------------------------------------------------------------------
# pymunk
# Copyright (c) 2007-2011 Victor Blomqvist
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
# ----------------------------------------------------------------------------
"""This module contain the Vec2d class that is used in all of pymunk when a
vector is needed.
The Vec2d class is used almost everywhere in pymunk for 2d coordinates and
vectors, for example to define gravity vector in a space. However, pymunk is
smart enough to convert tuples or tuple like objects to Vec2ds so you usually
do not need to explcitily do conversions if you happen to have a tuple::
>>> import pymunk
>>> space = pymunk.Space()
>>> print space.gravity
Vec2d(0.0, 0.0)
>>> space.gravity = 3,5
>>> print space.gravity
Vec2d(3.0, 5.0)
>>> space.gravity += 2,6
>>> print space.gravity
Vec2d(5.0, 11.0)
"""
__version__ = "$Id: vec2d.py 478 2013-01-21 11:01:39Z vb@viblo.se $"
__docformat__ = "reStructuredText"
import operator
import math
import ctypes
float_type = ctypes.c_double
__all__ = ["Vec2d"]
[docs]class Vec2d(ctypes.Structure):
"""2d vector class, supports vector and scalar operators, and also
provides some high level functions.
"""
__slots__ = ['x', 'y']
@classmethod
[docs] def from_param(cls, arg):
"""Used by ctypes to automatically create Vec2ds"""
return cls(arg)
[docs] def __init__(self, x_or_pair=None, y = None):
if x_or_pair != None:
if y == None:
self.x = x_or_pair[0]
self.y = x_or_pair[1]
else:
self.x = x_or_pair
self.y = y
def __len__(self):
return 2
def __getitem__(self, key):
if key == 0:
return self.x
elif key == 1:
return self.y
else:
raise IndexError("Invalid subscript "+str(key)+" to Vec2d")
def __setitem__(self, key, value):
if key == 0:
self.x = value
elif key == 1:
self.y = value
else:
raise IndexError("Invalid subscript "+str(key)+" to Vec2d")
# String representaion (for debugging)
def __repr__(self):
return 'Vec2d(%s, %s)' % (self.x, self.y)
# Comparison
def __eq__(self, other):
if hasattr(other, "__getitem__") and len(other) == 2:
return self.x == other[0] and self.y == other[1]
else:
return False
def __ne__(self, other):
if hasattr(other, "__getitem__") and len(other) == 2:
return self.x != other[0] or self.y != other[1]
else:
return True
def __nonzero__(self):
return self.x != 0.0 or self.y != 0.0
# Generic operator handlers
def _o2(self, other, f):
"Any two-operator operation where the left operand is a Vec2d"
if isinstance(other, Vec2d):
return Vec2d(f(self.x, other.x),
f(self.y, other.y))
elif (hasattr(other, "__getitem__")):
return Vec2d(f(self.x, other[0]),
f(self.y, other[1]))
else:
return Vec2d(f(self.x, other),
f(self.y, other))
def _r_o2(self, other, f):
"Any two-operator operation where the right operand is a Vec2d"
if (hasattr(other, "__getitem__")):
return Vec2d(f(other[0], self.x),
f(other[1], self.y))
else:
return Vec2d(f(other, self.x),
f(other, self.y))
def _io(self, other, f):
"inplace operator"
if (hasattr(other, "__getitem__")):
self.x = f(self.x, other[0])
self.y = f(self.y, other[1])
else:
self.x = f(self.x, other)
self.y = f(self.y, other)
return self
# Addition
def __add__(self, other):
if isinstance(other, Vec2d):
return Vec2d(self.x + other.x, self.y + other.y)
elif hasattr(other, "__getitem__"):
return Vec2d(self.x + other[0], self.y + other[1])
else:
return Vec2d(self.x + other, self.y + other)
__radd__ = __add__
def __iadd__(self, other):
if isinstance(other, Vec2d):
self.x += other.x
self.y += other.y
elif hasattr(other, "__getitem__"):
self.x += other[0]
self.y += other[1]
else:
self.x += other
self.y += other
return self
# Subtraction
def __sub__(self, other):
if isinstance(other, Vec2d):
return Vec2d(self.x - other.x, self.y - other.y)
elif (hasattr(other, "__getitem__")):
return Vec2d(self.x - other[0], self.y - other[1])
else:
return Vec2d(self.x - other, self.y - other)
def __rsub__(self, other):
if isinstance(other, Vec2d):
return Vec2d(other.x - self.x, other.y - self.y)
if (hasattr(other, "__getitem__")):
return Vec2d(other[0] - self.x, other[1] - self.y)
else:
return Vec2d(other - self.x, other - self.y)
def __isub__(self, other):
if isinstance(other, Vec2d):
self.x -= other.x
self.y -= other.y
elif (hasattr(other, "__getitem__")):
self.x -= other[0]
self.y -= other[1]
else:
self.x -= other
self.y -= other
return self
# Multiplication
def __mul__(self, other):
if isinstance(other, Vec2d):
return Vec2d(self.x*other.x, self.y*other.y)
if (hasattr(other, "__getitem__")):
return Vec2d(self.x*other[0], self.y*other[1])
else:
return Vec2d(self.x*other, self.y*other)
__rmul__ = __mul__
def __imul__(self, other):
if isinstance(other, Vec2d):
self.x *= other.x
self.y *= other.y
elif (hasattr(other, "__getitem__")):
self.x *= other[0]
self.y *= other[1]
else:
self.x *= other
self.y *= other
return self
# Division
def __div__(self, other):
return self._o2(other, operator.div)
def __rdiv__(self, other):
return self._r_o2(other, operator.div)
def __idiv__(self, other):
return self._io(other, operator.div)
def __floordiv__(self, other):
return self._o2(other, operator.floordiv)
def __rfloordiv__(self, other):
return self._r_o2(other, operator.floordiv)
def __ifloordiv__(self, other):
return self._io(other, operator.floordiv)
def __truediv__(self, other):
return self._o2(other, operator.truediv)
def __rtruediv__(self, other):
return self._r_o2(other, operator.truediv)
def __itruediv__(self, other):
return self._io(other, operator.truediv)
# Modulo
def __mod__(self, other):
return self._o2(other, operator.mod)
def __rmod__(self, other):
return self._r_o2(other, operator.mod)
def __divmod__(self, other):
return self._o2(other, divmod)
def __rdivmod__(self, other):
return self._r_o2(other, divmod)
# Exponentation
def __pow__(self, other):
return self._o2(other, operator.pow)
def __rpow__(self, other):
return self._r_o2(other, operator.pow)
# Bitwise operators
def __lshift__(self, other):
return self._o2(other, operator.lshift)
def __rlshift__(self, other):
return self._r_o2(other, operator.lshift)
def __rshift__(self, other):
return self._o2(other, operator.rshift)
def __rrshift__(self, other):
return self._r_o2(other, operator.rshift)
def __and__(self, other):
return self._o2(other, operator.and_)
__rand__ = __and__
def __or__(self, other):
return self._o2(other, operator.or_)
__ror__ = __or__
def __xor__(self, other):
return self._o2(other, operator.xor)
__rxor__ = __xor__
# Unary operations
def __neg__(self):
return Vec2d(operator.neg(self.x), operator.neg(self.y))
def __pos__(self):
return Vec2d(operator.pos(self.x), operator.pos(self.y))
def __abs__(self):
return Vec2d(abs(self.x), abs(self.y))
def __invert__(self):
return Vec2d(-self.x, -self.y)
# vectory functions
[docs] def get_length_sqrd(self):
"""Get the squared length of the vector.
It is more efficent to use this method instead of first call
get_length() or access .length and then do a sqrt().
:return: The squared length
"""
return self.x**2 + self.y**2
[docs] def get_length(self):
"""Get the length of the vector.
:return: The length
"""
return math.sqrt(self.x**2 + self.y**2)
def __setlength(self, value):
length = self.get_length()
self.x *= value/length
self.y *= value/length
length = property(get_length, __setlength,
doc = """Gets or sets the magnitude of the vector""")
[docs] def rotate(self, angle_radians):
"""Rotate the vector by angle_radians radians."""
cos = math.cos(angle_radians)
sin = math.sin(angle_radians)
x = self.x*cos - self.y*sin
y = self.x*sin + self.y*cos
self.x = x
self.y = y
[docs] def rotated(self, angle_radians):
"""Create and return a new vector by rotating this vector by
angle_radians radians.
:return: Rotated vector
"""
cos = math.cos(angle_radians)
sin = math.sin(angle_radians)
x = self.x*cos - self.y*sin
y = self.x*sin + self.y*cos
return Vec2d(x, y)
[docs] def rotate_degrees(self, angle_degrees):
"""Rotate the vector by angle_degrees degrees."""
self.rotate(math.radians(angle_degrees))
[docs] def rotated_degrees(self, angle_degrees):
"""Create and return a new vector by rotating this vector by
angle_degrees degrees.
:return: Rotade vector
"""
return self.rotated(math.radians(angle_degrees))
[docs] def get_angle(self):
if (self.get_length_sqrd() == 0):
return 0
return math.atan2(self.y, self.x)
def __setangle(self, angle):
self.x = self.length
self.y = 0
self.rotate(angle)
angle = property(get_angle, __setangle,
doc="""Gets or sets the angle (in radians) of a vector""")
[docs] def get_angle_degrees(self):
return math.degrees(self.get_angle())
def __set_angle_degrees(self, angle_degrees):
self.__setangle(math.radians(angle_degrees))
angle_degrees = property(get_angle_degrees, __set_angle_degrees,
doc="""Gets or sets the angle (in degrees) of a vector""")
[docs] def get_angle_between(self, other):
"""Get the angle between the vector and the other in radians
:return: The angle
"""
cross = self.x*other[1] - self.y*other[0]
dot = self.x*other[0] + self.y*other[1]
return math.atan2(cross, dot)
[docs] def get_angle_degrees_between(self, other):
"""Get the angle between the vector and the other in degrees
:return: The angle (in degrees)
"""
return math.degrees(self.get_angle_between(other))
[docs] def normalized(self):
"""Get a normalized copy of the vector
Note: This function will return 0 if the length of the vector is 0.
:return: A normalized vector
"""
length = self.length
if length != 0:
return self/length
return Vec2d(self)
[docs] def normalize_return_length(self):
"""Normalize the vector and return its length before the normalization
:return: The length before the normalization
"""
length = self.length
if length != 0:
self.x /= length
self.y /= length
return length
[docs] def perpendicular(self):
return Vec2d(-self.y, self.x)
[docs] def perpendicular_normal(self):
length = self.length
if length != 0:
return Vec2d(-self.y/length, self.x/length)
return Vec2d(self)
[docs] def dot(self, other):
"""The dot product between the vector and other vector
v1.dot(v2) -> v1.x*v2.x + v1.y*v2.y
:return: The dot product
"""
return float(self.x*other[0] + self.y*other[1])
[docs] def get_distance(self, other):
"""The distance between the vector and other vector
:return: The distance
"""
return math.sqrt((self.x - other[0])**2 + (self.y - other[1])**2)
[docs] def get_dist_sqrd(self, other):
"""The squared distance between the vector and other vector
It is more efficent to use this method than to call get_distance()
first and then do a sqrt() on the result.
:return: The squared distance
"""
return (self.x - other[0])**2 + (self.y - other[1])**2
[docs] def projection(self, other):
other_length_sqrd = other[0]*other[0] + other[1]*other[1]
projected_length_times_other_length = self.dot(other)
return other*(projected_length_times_other_length/other_length_sqrd)
[docs] def cross(self, other):
"""The cross product between the vector and other vector
v1.cross(v2) -> v1.x*v2.y - v2.y*v1.x
:return: The cross product
"""
return self.x*other[1] - self.y*other[0]
[docs] def interpolate_to(self, other, range):
return Vec2d(self.x + (other[0] - self.x)*range, self.y + (other[1] - self.y)*range)
[docs] def convert_to_basis(self, x_vector, y_vector):
x = self.dot(x_vector)/x_vector.get_length_sqrd()
y = self.dot(y_vector)/y_vector.get_length_sqrd()
return Vec2d(x, y)
def __get_int_xy(self):
return int(self.x), int(self.y)
int_tuple = property(__get_int_xy,
doc="""Return the x and y values of this vector as ints""")
@staticmethod
[docs] def zero():
"""A vector of zero length"""
return Vec2d(0, 0)
@staticmethod
[docs] def unit():
"""A unit vector pointing up"""
return Vec2d(0, 1)
@staticmethod
[docs] def ones():
"""A vector where both x and y is 1"""
return Vec2d(1, 1)
# Extra functions, mainly for chipmunk
[docs] def cpvrotate(self, other):
"""Uses complex multiplication to rotate this vector by the other. """
return Vec2d(self.x*other.x - self.y*other.y, self.x*other.y + self.y*other.x)
[docs] def cpvunrotate(self, other):
"""The inverse of cpvrotate"""
return Vec2d(self.x*other.x + self.y*other.y, self.y*other.x - self.x*other.y)
# Pickle
def __reduce__(self):
callable = Vec2d
args = (self.x, self.y)
return (callable, args)
Vec2d._fields_ = [
('x', float_type),
('y', float_type),
]
del float_type