Welcome, guest | Sign In | My Account | Store | Cart
from math import *
from functools import wraps

################################################################################

def autocast(method): # Optional method decorator
    @wraps(method)
    def wrapper(self, obj):
        try:
            return method(self, self.__class__(*obj))
        except TypeError:
            return method(self, obj)
    return wrapper

################################################################################

def Polar2(magnitude, degrees):
    x = magnitude * sin(radians(degrees))
    y = magnitude * cos(radians(degrees))
    return Vector2(x, y)

################################################################################

class Vector2:

    __slots__ = 'x', 'y'

    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __repr__(self):
        return 'Vector2({!r}, {!r})'.format(self.x, self.y)

    def polar_repr(self):
        x, y = self.x, self.y
        magnitude = hypot(x, y)
        angle = degrees(atan2(x, y)) % 360
        return 'Polar2({!r}, {!r})'.format(magnitude, angle)

    # Rich Comparison Methods

    def __lt__(self, obj):
        if isinstance(obj, Vector2):
            x1, y1, x2, y2 = self.x, self.y, obj.x, obj.y
            return x1 * x1 + y1 * y1 < x2 * x2 + y2 * y2
        return hypot(self.x, self.y) < obj

    def __le__(self, obj):
        if isinstance(obj, Vector2):
            x1, y1, x2, y2 = self.x, self.y, obj.x, obj.y
            return x1 * x1 + y1 * y1 <= x2 * x2 + y2 * y2
        return hypot(self.x, self.y) <= obj

    def __eq__(self, obj):
        if isinstance(obj, Vector2):
            return self.x == obj.x and self.y == obj.y
        return hypot(self.x, self.y) == obj

    def __ne__(self, obj):
        if isinstance(obj, Vector2):
            return self.x != obj.x or self.y != obj.y
        return hypot(self.x, self.y) != obj

    def __gt__(self, obj):
        if isinstance(obj, Vector2):
            x1, y1, x2, y2 = self.x, self.y, obj.x, obj.y
            return x1 * x1 + y1 * y1 > x2 * x2 + y2 * y2
        return hypot(self.x, self.y) > obj

    def __ge__(self, obj):
        if isinstance(obj, Vector2):
            x1, y1, x2, y2 = self.x, self.y, obj.x, obj.y
            return x1 * x1 + y1 * y1 >= x2 * x2 + y2 * y2
        return hypot(self.x, self.y) >= obj

    # Boolean Operation

    def __bool__(self):
        return self.x != 0 or self.y != 0

    # Container Methods

    def __len__(self):
        return 2

    def __getitem__(self, index):
        return (self.x, self.y)[index]

    def __setitem__(self, index, value):
        temp = [self.x, self.y]
        temp[index] = value
        self.x, self.y = temp

    def __iter__(self):
        yield self.x
        yield self.y

    def __reversed__(self):
        yield self.y
        yield self.x

    def __contains__(self, obj):
        return obj in (self.x, self.y)

    # Binary Arithmetic Operations

    def __add__(self, obj):
        if isinstance(obj, Vector2):
            return Vector2(self.x + obj.x, self.y + obj.y)
        return Vector2(self.x + obj, self.y + obj)

    def __sub__(self, obj):
        if isinstance(obj, Vector2):
            return Vector2(self.x - obj.x, self.y - obj.y)
        return Vector2(self.x - obj, self.y - obj)

    def __mul__(self, obj):
        if isinstance(obj, Vector2):
            return Vector2(self.x * obj.x, self.y * obj.y)
        return Vector2(self.x * obj, self.y * obj)

    def __truediv__(self, obj):
        if isinstance(obj, Vector2):
            return Vector2(self.x / obj.x, self.y / obj.y)
        return Vector2(self.x / obj, self.y / obj)

    def __floordiv__(self, obj):
        if isinstance(obj, Vector2):
            return Vector2(self.x // obj.x, self.y // obj.y)
        return Vector2(self.x // obj, self.y // obj)

    def __mod__(self, obj):
        if isinstance(obj, Vector2):
            return Vector2(self.x % obj.x, self.y % obj.y)
        return Vector2(self.x % obj, self.y % obj)

    def __divmod__(self, obj):
        if isinstance(obj, Vector2):
            return (Vector2(self.x // obj.x, self.y // obj.y),
                    Vector2(self.x % obj.x, self.y % obj.y))
        return (Vector2(self.x // obj, self.y // obj),
                Vector2(self.x % obj, self.y % obj))

    def __pow__(self, obj):
        if isinstance(obj, Vector2):
            return Vector2(self.x ** obj.x, self.y ** obj.y)
        return Vector2(self.x ** obj, self.y ** obj)

    def __lshift__(self, obj):
        if isinstance(obj, Vector2):
            return Vector2(self.x << obj.x, self.y << obj.y)
        return Vector2(self.x << obj, self.y << obj)

    def __rshift__(self, obj):
        if isinstance(obj, Vector2):
            return Vector2(self.x >> obj.x, self.y >> obj.y)
        return Vector2(self.x >> obj, self.y >> obj)

    def __and__(self, obj):
        if isinstance(obj, Vector2):
            return Vector2(self.x & obj.x, self.y & obj.y)
        return Vector2(self.x & obj, self.y & obj)

    def __xor__(self, obj):
        if isinstance(obj, Vector2):
            return Vector2(self.x ^ obj.x, self.y ^ obj.y)
        return Vector2(self.x ^ obj, self.y ^ obj)

    def __or__(self, obj):
        if isinstance(obj, Vector2):
            return Vector2(self.x | obj.x, self.y | obj.y)
        return Vector2(self.x | obj, self.y | obj)

    # Binary Arithmetic Operations (with reflected operands)

    def __radd__(self, obj):
        return Vector2(obj + self.x, obj + self.y)

    def __rsub__(self, obj):
        return Vector2(obj - self.x, obj - self.y)

    def __rmul__(self, obj):
        return Vector2(obj * self.x, obj * self.y)

    def __rtruediv__(self, obj):
        return Vector2(obj / self.x, obj / self.y)

    def __rfloordiv__(self, obj):
        return Vector2(obj // self.x, obj // self.y)

    def __rmod__(self, obj):
        return Vector2(obj % self.x, obj % self.y)

    def __rdivmod__(self, obj):
        return (Vector2(obj // self.x, obj // self.y),
                Vector2(obj % self.x, obj % self.y))

    def __rpow__(self, obj):
        return Vector2(obj ** self.x, obj ** self.y)

    def __rlshift__(self, obj):
        return Vector2(obj << self.x, obj << self.y)

    def __rrshift__(self, obj):
        return Vector2(obj >> self.x, obj >> self.y)

    def __rand__(self, obj):
        return Vector2(obj & self.x, obj & self.y)

    def __rxor__(self, obj):
        return Vector2(obj ^ self.x, obj ^ self.y)

    def __ror__(self, obj):
        return Vector2(obj | self.x, obj | self.y)

    # Augmented Arithmetic Assignments

    def __iadd__(self, obj):
        if isinstance(obj, Vector2):
            self.x += obj.x
            self.y += obj.y
        else:
            self.x += obj
            self.y += obj
        return self

    def __isub__(self, obj):
        if isinstance(obj, Vector2):
            self.x -= obj.x
            self.y -= obj.y
        else:
            self.x -= obj
            self.y -= obj
        return self

    def __imul__(self, obj):
        if isinstance(obj, Vector2):
            self.x *= obj.x
            self.y *= obj.y
        else:
            self.x *= obj
            self.y *= obj
        return self

    def __itruediv__(self, obj):
        if isinstance(obj, Vector2):
            self.x /= obj.x
            self.y /= obj.y
        else:
            self.x /= obj
            self.y /= obj
        return self

    def __ifloordiv__(self, obj):
        if isinstance(obj, Vector2):
            self.x //= obj.x
            self.y //= obj.y
        else:
            self.x //= obj
            self.y //= obj
        return self

    def __imod__(self, obj):
        if isinstance(obj, Vector2):
            self.x %= obj.x
            self.y %= obj.y
        else:
            self.x %= obj
            self.y %= obj
        return self

    def __ipow__(self, obj):
        if isinstance(obj, Vector2):
            self.x **= obj.x
            self.y **= obj.y
        else:
            self.x **= obj
            self.y **= obj
        return self

    def __ilshift__(self, obj):
        if isinstance(obj, Vector2):
            self.x <<= obj.x
            self.y <<= obj.y
        else:
            self.x <<= obj
            self.y <<= obj
        return self

    def __irshift__(self, obj):
        if isinstance(obj, Vector2):
            self.x >>= obj.x
            self.y >>= obj.y
        else:
            self.x >>= obj
            self.y >>= obj
        return self

    def __iand__(self, obj):
        if isinstance(obj, Vector2):
            self.x &= obj.x
            self.y &= obj.y
        else:
            self.x &= obj
            self.y &= obj
        return self

    def __ixor__(self, obj):
        if isinstance(obj, Vector2):
            self.x ^= obj.x
            self.y ^= obj.y
        else:
            self.x ^= obj
            self.y ^= obj
        return self

    def __ior__(self, obj):
        if isinstance(obj, Vector2):
            self.x |= obj.x
            self.y |= obj.y
        else:
            self.x |= obj
            self.y |= obj
        return self

    # Unary Arithmetic Operations

    def __pos__(self):
        return Vector2(+self.x, +self.y)

    def __neg__(self):
        return Vector2(-self.x, -self.y)

    def __invert__(self):
        return Vector2(~self.x, ~self.y)

    def __abs__(self):
        return Vector2(abs(self.x), abs(self.y))

    # Virtual "magnitude" Attribute

    def __fg_ma(self):
        return hypot(self.x, self.y)

    def __fs_ma(self, value):
        x, y = self.x, self.y
        temp = value / hypot(x, y)
        self.x, self.y = x * temp, y * temp

    magnitude = property(__fg_ma, __fs_ma, doc='Virtual "magnitude" Attribute')

    # Virtual "direction" Attribute

    def __fg_di(self):
        return atan2(self.y, self.x)

    def __fs_di(self, value):
        temp = hypot(self.x, self.y)
        self.x, self.y = cos(value) * temp, sin(value) * temp

    direction = property(__fg_di, __fs_di, doc='Virtual "direction" Attribute')

    # Virtual "degrees" Attribute

    def __fg_de(self):
        return degrees(atan2(self.x, self.y)) % 360

    def __fs_de(self, value):
        value, temp = radians(value), hypot(self.x, self.y)
        self.x, self.y = sin(value) * temp, cos(value) * temp

    degrees = property(__fg_de, __fs_de, doc='Virtual "degrees" Attribute')

    # Virtual "xy" Attribute

    def __fg_xy(self):
        return self.x, self.y

    def __fs_xy(self, value):
        self.x, self.y = value

    xy = property(__fg_xy, __fs_xy, doc='Virtual "xy" Attribute')

    # Virtual "yx" Attribute

    def __fg_yx(self):
        return self.y, self.x

    def __fs_yx(self, value):
        self.y, self.x = value

    yx = property(__fg_yx, __fs_yx, doc='Virtual "yx" Attribute')

    # Unit Vector Operations

    def unit_vector(self):
        x, y = self.x, self.y
        temp = hypot(x, y)
        return Vector2(x / temp, y / temp)

    def normalize(self):
        x, y = self.x, self.y
        temp = hypot(x, y)
        self.x, self.y = x / temp, y / temp
        return self

    # Vector Multiplication Operations

    def dot_product(self, vec):
        return self.x * vec.x + self.y * vec.y

    def cross_product(self, vec):
        return self.x * vec.y - self.y * vec.x

    # Geometric And Physical Reflections

    def reflect(self, vec):
        x1, y1, x2, y2 = self.x, self.y, vec.x, vec.y
        temp = 2 * (x1 * x2 + y1 * y2) / (x2 * x2 + y2 * y2)
        return Vector2(x2 * temp - x1, y2 * temp - y1)

    def bounce(self, vec):
        x1, y1, x2, y2 = self.x, self.y, +vec.y, -vec.x
        temp = 2 * (x1 * x2 + y1 * y2) / (x2 * x2 + y2 * y2)
        return Vector2(x2 * temp - x1, y2 * temp - y1)

    # Standard Vector Operations

    def project(self, vec):
        x, y = vec.x, vec.y
        temp = (self.x * x + self.y * y) / (x * x + y * y)
        return Vector2(x * temp, y * temp)

    def rotate(self, vec):
        x1, y1, x2, y2 = self.x, self.y, vec.x, vec.y
        return Vector2(x1 * x2 + y1 * y2, y1 * x2 - x1 * y2)

    def interpolate(self, vec, bias):
        a = 1 - bias
        return Vector2(self.x * a + vec.x * bias, self.y * a + vec.y * bias)

    # Other Useful Methods

    def near(self, vec, dist):
        x, y = self.x - vec.x, self.y - vec.y
        return x * x + y * y <= dist * dist

    def perpendicular(self):
        return Vector2(+self.y, -self.x)

    def subset(self, vec1, vec2):
        x1, x2 = vec1.x, vec2.x
        if x1 <= x2:
            if x1 <= self.x <= x2:
                y1, y2 = vec1.y, vec2.y
                if y1 <= y2:
                    return y1 <= self.y <= y2
                return y2 <= self.y <= y1
        else:
            if x2 <= self.x <= x1:
                y1, y2 = vec1.y, vec2.y
                if y1 <= y2:
                    return y1 <= self.y <= y2
                return y2 <= self.y <= y1
        return False

    def distance(self, vec):
        return hypot(self.x - vec.x, self.y - vec.y)

    def limit(self, dist):
        x, y = self.x, self.y
        magnitude = hypot(x, y)
        if magnitude > dist:
            temp = dist / magnitude
            self.x, self.y = x * temp, y * temp
        return self

    def direction_between(self, vec):
        return atan2(self.y, self.x) - atan2(vec.y, vec.x)

    def degrees_between(self, vec):
        diff = degrees(atan2(self.y, self.x) - atan2(vec.y, vec.x)) % 360
        return 360 - diff if diff > 180 else diff

    # Synonymous Definitions

    copy = __pos__

    inverse = __neg__

    unit = unit_vector

    dot = dot_product

    cross = cross_product

    lerp = interpolate

    perp = perpendicular

    dist = distance

    dir_diff = direction_between

    deg_diff = degrees_between

################################################################################

import recipe576904; recipe576904.bind_all(globals())

History