Download the release version here:
neg-world-engine.zip
Or download the newest version from the Git repository:
https://gitlab.tubit.tu-berlin.de/srather/NEG-World-Engine.git
import math class Vector(tuple): """ 2d-Vector class for calculations. A 'Vector' object is a 2d-tuple with 'self[0]' and 'self[1]' accessable as 'self.x' and 'self.y'. There are also numerus methods to calculate with vectors. """ def __new__(cls, x, y): """ Return a new tuple '(x, y)' for parent of new 'Vector' object. """ if type(x) in (int, float) and type(y) in (int, float): return tuple.__new__(cls, (x, y)) try: return tuple.__new__(cls, (float(x), float(y))) except TypeError: raise TypeError(f"'x' and 'y' of vector have to be type 'int' or 'float'") def __getattr__(self, attr): """ Return 'self[0]' and 'self[1]' as attribute 'x' and 'y'. """ if attr == 'x': return self[0] if attr == 'y': return self[1] raise AttributeError(f"{self.__class__.__name__} has no Attribute {attr}.") def __copy__(self): """Return a copy of 'self'.""" return Vector(*self) def __bool__(self): """Return 'True' if 'self' is nonzero.""" return bool(round(self.x, 5)) or bool(round(self.y, 5)) def __eq__(self, other): """ Return 'True' if 'self' and 'other' are equal. """ if type(other) is Vector: return round(self.x, 5) == round(other.x, 5) and round(self.y, 5) == round(other.y, 5) else: return False def __ne__(self, other): """Return 'True' if 'self' and 'other' are not equal.""" return not self == other def __neg__(self): """Return the additive inverse of 'self'.""" return Vector(-self.x, -self.y) def __invert__(self): """Swiches 'x' and 'y'.""" return Vector(self.y, self.x) def __round__(self, d=None): """Return 'self' with both components rounded to 'd' decimal places.""" return Vector(round(self.x, d), round(self.y, d)) def __add__(self, other): """ Return the sum of vectors 'self' and 'other'. """ if type(other) is Vector: return Vector(self.x + other.x, self.y + other.y) if not bool(other): # for sum([vectors..]) to work return self return NotImplemented def __radd__(self, other): """Return the sum of vectors 'self' and 'other'.""" return self + other def __sub__(self, other): """Return the difference of vectors 'self' and 'other'.""" return self + (-other) def __mul__(self, other): """ If other is a integer or float Return a scaled vector of 'self' by factor 'other'. If other is a vector: Multiplies both components individually. """ if type(other) in (int, float): return Vector(self.x * other, self.y * other) if type(other) is Vector: return Vector(self.x * other.x, self.y * other.y) return NotImplemented def __rmul__(self, value): """Return a scaled vector of 'self' by factor 'value'.""" return self * value def __truediv__(self, other): """ If other is a integer or float: Return a scaled vector of 'self' by factor '1 / other'. If other is a vector: Return 'value' with 'other * value == self'. Return 'None' if both are linear independant. """ if type(other) in (int, float): return self * (1 / other) if type(other) is Vector: value = 0 if other.x != 0: value = self.x / other.x elif other.y != 0: value = self.y / other.y if round(other * value, 5) == round(self, 5): return value return None return NotImplemented def __str__(self): """Return a string representing 'self' in style of '"<x, y>"'.""" return f"<{tuple.__repr__(self)[1:-1]}>" def __repr__(self): """Return a string representing 'self' in style of '"<x, y>"'.""" return str(self) def length(self): """Return the euclidean length of vector 'self'.""" return math.sqrt(self.x * self.x + self.y * self.y) def length2(self): """Return the inf-norm of vector 'self'.""" return max(abs(self.x), abs(self.y)) def multiple_of(self, other): """ Return 'True', if vetors are linear depentant. """ if type(other) is Vector: return round(self.x * other.y, 5) == round(self.y * other.x, 5) # determinant is zero raise TypeError(f"Expected a vector, but {other.__class__.__name__} found") def angle(self): """ Return the angle of vector 'self' in radiants, starting at 0 for positive x direction, incresing counterclockwise. """ return math.atan2(self.y, self.x) def angle2(self): """ Return the angle of vector 'self' in degree, starting at 0 for positive x direction, incresing counterclockwise. """ return math.atan2(self.y, self.x) / math.pi * 180 def is_between(self, vec1, vec2): """ Return 'True' if the angle of 'self' is between 2 other vectors. This starts at 'vec1' rotating counterclockwise, checking if the angle of 'self' is reched before the angle of 'vec2'. """ if not self: # == 0 return True if vec1 == self or vec2 == self: return True # vec1 <= self <= vec2 if vec1.angle() <= self.angle() and self.angle() <= vec2.angle(): return True # vec2 < vec1 if vec2.angle() < vec1.angle(): # !(vec2 < self < vec1) if self.angle() <= vec2.angle() or vec1.angle() <= self.angle(): return True return False def dot(self, other): """Return the dot product of 'self' and 'other'.""" return sum(self * other) def cross(self, other): """Return the cross product of 'self' and 'other'.""" return self.x * other.y - self.y * other.x def normal(self): """Return a vector orthogonal to 'self'.""" return Vector(-self.y, self.x) def normal2(self): """Return a vector orthogonal to 'self'.""" return Vector(self.y, -self.x) def angle_to(self, other): """ Return angle in degree from 'self' to 'other' counterclockwise. """ if type(other) is Vector: if self.angle() <= other.angle(): return other.angle2() - self.angle2() else: return other.angle2() + 360 - self.angle2() raise TypeError(f"Expected a vector, but {other.__class__.__name__} found") def normalised(self): """ Return a vector of length '1' with the same angle as 'self'. """ if self.length() == 0: return self return round(self / self.length(), 5) def onscreen(self, screen, offset): """ Return 'True' if 'self' is within the rect 'screen' with given 'offset'. """ rect = screen.get_rect() pos = round(self - offset + Vector(*rect[2:]) / 2) if pos.x < rect[0] or pos.y < rect[1] or pos.x >= rect[2] or pos.y >= rect[3]: return False return True
from vector import * class Matrix(tuple): """ 2d-Matrix class for calculations. A 'Matrix' object is a 2-element list of vectors with 'self[0]' and 'self[1]' accessable as 'self.i' and 'self.j'. There are also numerus methods to calculate with vectors. """ def __new__(cls, i, j): """ Return a new tuple '(i, j)' for parent of new 'Matrix' object. """ try: return tuple.__new__(cls, (Vector(*i), Vector(*j))) except: raise TypeError(f"'i' and 'j' of matrix have to be type 'Vector'") def __getattr__(self, attr): """ Return 'self[0]' and 'self[1]' as attribute 'i' and 'j'. """ if attr == 'i': return self[0] if attr == 'j': return self[1] raise AttributeError(f"{self.__class__.__name__} has no Attribute {attr}.") def __copy__(self): """Return a copy of 'self'.""" return Matrix(*self) def __bool__(self): """Return 'True' if 'self' is nonzero.""" return bool(self.i) or bool(self.j) def __eq__(self, other): """ Return 'True' if 'self' and 'other' are equal. """ if type(other) is Matrix: return self.i == other.i and self.j == other.j else: return False def __ne__(self, other): """Return 'True' if 'self' and 'other' are not equal.""" return not self == other def __neg__(self): """Return the additive inverse of 'self'.""" return Matrix(-self.i, -self.j) def __invert__(self): """Swiches 'i' and 'j'.""" return Matrix(self.j, self.i) def __round__(self, d=None): """Return 'self' with both components rounded to 'd' decimal places.""" return Vector(round(self.i, d), round(self.j, d)) def __add__(self, other): """ Return the sum of matrices 'self' and 'other'. """ if type(other) is Matrix: return Matrix(self.i + other.i, self.j + other.j) if type(other) in (int, float): return Matrix(self.i + Vector(other, 0), self.j + Vector(0, other)) return NotImplemented def __radd__(self, other): """Return the sum of matrices 'self' and 'other'.""" return self + other def __sub__(self, other): """Return the difference of matrices 'self' and 'other'.""" return self + (-other) def __mul__(self, other): """ If other is a integer or float Return a scaled matrix of 'self' by factor 'other'. If other is a matrix: Multiplies both components individually. """ if type(other) in (int, float): return Matrix(self.i * other, self.j * other) if type(other) is Vector: return Matrix(self.i * other.i, self.j * other.j) return NotImplemented def __rmul__(self, value): """Return a scaled vector of 'self' by factor 'value'.""" return self * value def __truediv__(self, other): """ If other is a integer or float: Return a scaled vector of 'self' by factor '1 / other'. If other is a vector: Return 'value' with 'other * value == self'. Return 'None' if both are linear independant. """ if type(other) in (int, float): return self * (1 / other) if type(other) is Vector: value = 0 if other.x != 0: value = self.x / other.x elif other.y != 0: value = self.y / other.y if round(other * value, 5) == round(self, 5): return value return None return NotImplemented def __str__(self): """Return a string representing 'self' in style of '"<i, j>"'.""" return f"<{tuple.__repr__(self)[1:-1]}>" def __repr__(self): """Return a string representing 'self' in style of '"<i, j>"'.""" return str(self) def T(self): """Return the transposed matrix of 'self'.""" return Matrix((self.i.x, self.j.x), (self.i.y, self.j.y)) def det(self): """Return determinant of self.""" return self.i.x * self.j.y - self.i.y * self.j.x def inverse(self): """Return the inverse of self.""" return Matrix((-self.j.y, self.i.y), (self.j.x, -self.i.x)) / -self.det() def prod(self, other): """ Return the matrix multiplication of 'self' and 'other'. """ if type(other) is Vector: return Vector(self.T().i.dot(other), self.T().j.dot(other)) if type(other) is Matrix: return Matrix(self.prod(other.i), self.prod(other.j)) return NotImplemented