Source code for freegames.utils

"""Utilities
"""
# pylint: disable=no-member

import collections.abc
import math
import os


[docs]def floor(value, size, offset=200): """Floor of `value` given `size` and `offset`. The floor function is best understood with a diagram of the number line:: -200 -100 0 100 200 <--|--x--|-----|--y--|--z--|--> The number line shown has offset 200 denoted by the left-hand tick mark at -200 and size 100 denoted by the tick marks at -100, 0, 100, and 200. The floor of a value is the left-hand tick mark of the range where it lies. So for the points show above: ``floor(x)`` is -200, ``floor(y)`` is 0, and ``floor(z)`` is 100. >>> floor(10, 100) 0.0 >>> floor(120, 100) 100.0 >>> floor(-10, 100) -100.0 >>> floor(-150, 100) -200.0 >>> floor(50, 167) -33.0 """ return float(((value + offset) // size) * size - offset)
[docs]def path(filename): """Return full path to `filename` in freegames module.""" filepath = os.path.realpath(__file__) dirpath = os.path.dirname(filepath) fullpath = os.path.join(dirpath, filename) return fullpath
[docs]def line(a, b, x, y): """Draw line from `(a, b)` to `(x, y)`.""" import turtle turtle.up() turtle.goto(a, b) turtle.down() turtle.goto(x, y)
[docs]def square(x, y, size, name): """Draw square at `(x, y)` with side length `size` and fill color `name`. The square is oriented so the bottom left corner is at (x, y). """ import turtle turtle.up() turtle.goto(x, y) turtle.down() turtle.color(name) turtle.begin_fill() for count in range(4): turtle.forward(size) turtle.left(90) turtle.end_fill()
[docs]class vector(collections.abc.Sequence): """Two-dimensional vector. Vectors can be modified in-place. >>> v = vector(0, 1) >>> v.move(1) >>> v vector(1, 2) >>> v.rotate(90) >>> v vector(-2.0, 1.0) """ # pylint: disable=invalid-name PRECISION = 6 __slots__ = ('_x', '_y', '_hash')
[docs] def __init__(self, x, y): """Initialize vector with coordinates: x, y. >>> v = vector(1, 2) >>> v.x 1 >>> v.y 2 """ self._hash = None self._x = round(x, self.PRECISION) self._y = round(y, self.PRECISION)
@property def x(self): """X-axis component of vector. >>> v = vector(1, 2) >>> v.x 1 >>> v.x = 3 >>> v.x 3 """ return self._x @x.setter def x(self, value): if self._hash is not None: raise ValueError('cannot set x after hashing') self._x = round(value, self.PRECISION) @property def y(self): """Y-axis component of vector. >>> v = vector(1, 2) >>> v.y 2 >>> v.y = 5 >>> v.y 5 """ return self._y @y.setter def y(self, value): if self._hash is not None: raise ValueError('cannot set y after hashing') self._y = round(value, self.PRECISION)
[docs] def __hash__(self): """v.__hash__() -> hash(v) >>> v = vector(1, 2) >>> h = hash(v) >>> v.x = 2 Traceback (most recent call last): ... ValueError: cannot set x after hashing """ if self._hash is None: pair = (self.x, self.y) self._hash = hash(pair) return self._hash
[docs] def set(self, other): """Set vector x, y to other x, y. >>> a = vector(0, 0) >>> b = vector(1, 2) >>> a.set(b) >>> a vector(1, 2) """ self.x = other.x self.y = other.y
[docs] def __len__(self): """v.__len__() -> len(v) >>> v = vector(1, 2) >>> len(v) 2 """ return 2
[docs] def __getitem__(self, index): """v.__getitem__(v, i) -> v[i] >>> v = vector(3, 4) >>> v[0] 3 >>> v[1] 4 >>> v[2] Traceback (most recent call last): ... IndexError """ if index == 0: return self.x if index == 1: return self.y raise IndexError
[docs] def copy(self): """Return copy of vector. >>> v = vector(1, 2) >>> w = v.copy() >>> v is w False """ type_self = type(self) return type_self(self.x, self.y)
[docs] def __eq__(self, other): """v.__eq__(w) -> v == w >>> v = vector(1, 2) >>> w = vector(1, 2) >>> v == w True """ if isinstance(other, vector): return self.x == other.x and self.y == other.y return NotImplemented
[docs] def __ne__(self, other): """v.__ne__(w) -> v != w >>> v = vector(1, 2) >>> w = vector(3, 4) >>> v != w True """ if isinstance(other, vector): return self.x != other.x or self.y != other.y return NotImplemented
[docs] def __iadd__(self, other): """v.__iadd__(w) -> v += w >>> v = vector(1, 2) >>> w = vector(3, 4) >>> v += w >>> v vector(4, 6) >>> v += 1 >>> v vector(5, 7) """ if self._hash is not None: raise ValueError('cannot add vector after hashing') if isinstance(other, vector): self.x += other.x self.y += other.y else: self.x += other self.y += other return self
[docs] def __add__(self, other): """v.__add__(w) -> v + w >>> v = vector(1, 2) >>> w = vector(3, 4) >>> v + w vector(4, 6) >>> v + 1 vector(2, 3) >>> 2.0 + v vector(3.0, 4.0) """ copy = self.copy() return copy.__iadd__(other)
__radd__ = __add__
[docs] def move(self, other): """Move vector by other (in-place). >>> v = vector(1, 2) >>> w = vector(3, 4) >>> v.move(w) >>> v vector(4, 6) >>> v.move(3) >>> v vector(7, 9) """ self.__iadd__(other)
[docs] def __isub__(self, other): """v.__isub__(w) -> v -= w >>> v = vector(1, 2) >>> w = vector(3, 4) >>> v -= w >>> v vector(-2, -2) >>> v -= 1 >>> v vector(-3, -3) """ if self._hash is not None: raise ValueError('cannot subtract vector after hashing') if isinstance(other, vector): self.x -= other.x self.y -= other.y else: self.x -= other self.y -= other return self
[docs] def __sub__(self, other): """v.__sub__(w) -> v - w >>> v = vector(1, 2) >>> w = vector(3, 4) >>> v - w vector(-2, -2) >>> v - 1 vector(0, 1) """ copy = self.copy() return copy.__isub__(other)
[docs] def __imul__(self, other): """v.__imul__(w) -> v *= w >>> v = vector(1, 2) >>> w = vector(3, 4) >>> v *= w >>> v vector(3, 8) >>> v *= 2 >>> v vector(6, 16) """ if self._hash is not None: raise ValueError('cannot multiply vector after hashing') if isinstance(other, vector): self.x *= other.x self.y *= other.y else: self.x *= other self.y *= other return self
[docs] def __mul__(self, other): """v.__mul__(w) -> v * w >>> v = vector(1, 2) >>> w = vector(3, 4) >>> v * w vector(3, 8) >>> v * 2 vector(2, 4) >>> 3.0 * v vector(3.0, 6.0) """ copy = self.copy() return copy.__imul__(other)
__rmul__ = __mul__
[docs] def scale(self, other): """Scale vector by other (in-place). >>> v = vector(1, 2) >>> w = vector(3, 4) >>> v.scale(w) >>> v vector(3, 8) >>> v.scale(0.5) >>> v vector(1.5, 4.0) """ self.__imul__(other)
[docs] def __itruediv__(self, other): """v.__itruediv__(w) -> v /= w >>> v = vector(2, 4) >>> w = vector(4, 8) >>> v /= w >>> v vector(0.5, 0.5) >>> v /= 2 >>> v vector(0.25, 0.25) """ if self._hash is not None: raise ValueError('cannot divide vector after hashing') if isinstance(other, vector): self.x /= other.x self.y /= other.y else: self.x /= other self.y /= other return self
[docs] def __truediv__(self, other): """v.__truediv__(w) -> v / w >>> v = vector(1, 2) >>> w = vector(3, 4) >>> w / v vector(3.0, 2.0) >>> v / 2 vector(0.5, 1.0) """ copy = self.copy() return copy.__itruediv__(other)
[docs] def __neg__(self): """v.__neg__() -> -v >>> v = vector(1, 2) >>> -v vector(-1, -2) """ # pylint: disable=invalid-unary-operand-type copy = self.copy() copy.x = -copy.x copy.y = -copy.y return copy
[docs] def __abs__(self): """v.__abs__() -> abs(v) >>> v = vector(3, 4) >>> abs(v) 5.0 """ return (self.x**2 + self.y**2) ** 0.5
[docs] def rotate(self, angle): """Rotate vector counter-clockwise by angle (in-place). >>> v = vector(1, 2) >>> v.rotate(90) >>> v == vector(-2, 1) True """ if self._hash is not None: raise ValueError('cannot rotate vector after hashing') radians = angle * math.pi / 180.0 cosine = math.cos(radians) sine = math.sin(radians) x = self.x y = self.y self.x = x * cosine - y * sine self.y = y * cosine + x * sine
[docs] def __repr__(self): """v.__repr__() -> repr(v) >>> v = vector(1, 2) >>> repr(v) 'vector(1, 2)' """ type_self = type(self) name = type_self.__name__ return '{}({!r}, {!r})'.format(name, self.x, self.y)