Module generativepy.math
Expand source code
# Author: Martin McBride
# Created: 2023-02-06
# Copyright (C) 2023, Martin McBride
# License: MIT
import math
"""
The math module provides basic implementation of 2D vectors and matrices.
There are other Python matrix libraries, but this library is geared towards vector graphics, and provides features such
as polar vectors and vector lerp (linear interpolation) that are useful for maths visualisation and animation.
"""
def isclose(a, b, rel_tol=1e-09, abs_tol=1e-12):
"""
Check if two values a and b are equal to within a given tolerance
Args:
a: number - First value
b: number - Second value
rel_tol: number - Tolerance as a fraction of the absolute value of a or b (whichever is largest)
abs_tol: number - Tolerance as an absolute value
Returns:
True if the numbers are close, false otherwise.
"""
return abs(a - b) <= max(rel_tol * max(abs(a), abs(b)), abs_tol)
class Matrix():
"""
Class to represent a 2D transform matrix:
```
| xx xy xt |
| yx yy yt |
```
"""
@staticmethod
def unit():
"""
Create a unit matrix
Returns:
The unit matrix.
"""
return Matrix(1, 0, 0, 0, 1, 0)
@staticmethod
def scale(scale_x, scale_y=None):
"""
Create a scaling matrix
Args:
scale_x: Scale factor in x direction
scale_y: Scale factor in y direction, defaults to scale_x
Returns:
New matrix
"""
if scale_y is None:
scale_y = scale_x
return Matrix(scale_x, 0, 0, 0, scale_y, 0)
@staticmethod
def translate(x, y):
"""
Create a translation matrix
Args:
x: Translation in x direction
y: Translation in y direction
Returns:
New matrix
"""
return Matrix(1, 0, x, 0, 1, y)
@staticmethod
def rotate(angle):
"""
Create a rotation matrix
Args:
angle: Angle in radians, measured counterclockwise from positive x direction
Returns:
New matrix
"""
c = math.cos(angle)
s = math.sin(angle)
return Matrix(c, -s, 0, s, c, 0)
@staticmethod
def multiply(p, q):
"""
Multiply two matrices
Args:
a: First matrix
b: Second matrix
Returns:
New matrix
"""
a = p[0] * q[0] + p[1] * q[3]
b = p[0] * q[1] + p[1] * q[4]
c = p[0] * q[2] + p[1] * q[5] + p[2]
d = p[3] * q[0] + p[4] * q[3]
e = p[3] * q[1] + p[4] * q[4]
f = p[3] * q[2] + p[4] * q[5] + p[5]
return Matrix(a, b, c, d, e, f)
def __init__(self, xx, xy, xt, yx, yy, yt):
self.matrix = (xx, xy, xt, yx, yy, yt)
def __iter__(self):
return iter(self.matrix)
def __len__(self):
return len(self.matrix)
def __getitem__(self, index):
return self.matrix[index]
def __eq__(self, other):
return all([isclose(a, b) for a, b in zip(self, other)])
def __neg__(self):
return self * -1
def __add__(self, other):
return Matrix(*[a + b for a, b in zip(self, other)])
def __sub__(self, other):
# add the negative of `other`
return self + (-other)
def __mul__(self, other):
# matrix * scalar
if isinstance(other, (int, float)):
return Matrix(*[other * a for a in self])
if isinstance(other, Matrix):
return Matrix.multiply(self, other)
return NotImplemented
def __rmul__(self, other):
return self.__mul__(other)
def __truediv__(self, other):
# matrix / scalar
if isinstance(other, (int, float)):
return Matrix(*[a / other for a in self])
else:
return NotImplemented
def __floordiv__(self, other):
# matrix // scalar
if isinstance(other, (int, float)):
return Matrix(*[a // other for a in self])
else:
return NotImplemented
def __repr__(self):
return "Matrix({0}, {1}, {2}, {3}, {4}, {5})".format(*self.matrix)
def __str__(self):
return repr(self)
class Vector():
"""
Class to represent a 2-vector including most of its common operations
This is based on easy_vector https://github.com/DariusMontez/easy_vector
The main changes are to make the object immutable, and measuring angles in radians rather than degrees
"""
@staticmethod
def polar(length, angle):
"""
Create a vector based on a length and angle
Args:
length: Length of vector
angle: Angle in radians, measured counterclockwise from positive x direction
Returns:
New vector
"""
x = length * math.cos(angle)
y = length * math.sin(angle)
return Vector(x, y)
@staticmethod
def matrix_premultiply(m, v):
"""
Multiply a matrix (first) and a vector (second)
Args:
m: matrix
v: vector
Returns:
New vector
"""
a = m[0] * v[0] + m[1] * v[1] + m[2]
b = m[3] * v[0] + m[4] * v[1] + m[5]
return Vector(a, b)
def __init__(self, *args):
"""
Can either accept 2 number, or a tuple containing 2 numerical elements.
Args:
args: various - see above
Returns:
Self
"""
if len(args) == 1 and hasattr(args[0], "__iter__") and len(args[0]) == 2:
self.coords = tuple(args[0])
elif len(args) == 2 and isinstance(args[0], (int, float)) and isinstance(args[1], (int, float)):
self.coords = tuple(args)
else:
raise ValueError("Vector requires a sequence of length 2, or 2 numbers")
def transform(self, m):
"""
Transform this vector by a matrix. The vector is pre-multiplied by the matrix
Args:
m: matrix
Returns:
New transformed vector
"""
return m * self
def scale(self, scale_x, scale_y=None):
"""
Scale this vector by a factor.
Args:
scale_x: scale factor in x direction.
scale_y: scale factor in y direction. If this is None, scale by scale_x in both directions.
Returns:
New scaled vector
"""
return Matrix.scale(scale_x, scale_y) * self
def translate(self, x, y):
"""
Translate this vector by (x, y),
Args:
x: translation amount in x direction.
y: translation amount in y direction.
Returns:
New translated vector
"""
return Matrix.translate(x, y) * self
def rotate(self, angle):
"""
Rotate this vector by (x, y),
Args:
x: rotation amount in x direction.
y: rotation amount in y direction.
Returns:
New rotated vector
"""
return Matrix.rotate(angle) * self
def lerp(self, other, factor):
"""
Interpolate between this vector and other.
The `factor` parameter works like this:
* 0 - result is self
* 1 - result is other
* 0 to 1 - result between self and other
* > 1 - result extensds beyond other
* < 0 - result extends backwards before other
Args:
other: Vector - the other vector
factor: number - The interpolation amount.
Returns:
New rotated vector
"""
return Vector((1 - factor) * self.x + factor * other.x, (1 - factor) * self.y + factor * other.y)
def __iter__(self):
return iter(self.coords)
def __len__(self):
return len(self.coords)
def __getitem__(self, index):
return self.coords[index]
def __eq__(self, other):
return isclose(self.x, other.x) and isclose(self.y, other.y)
def __neg__(self):
return self * -1
def __add__(self, other):
return Vector(self.x + other.x, self.y + other.y)
def __sub__(self, other):
# add the negative of `other`
return self + (-other)
def __mul__(self, other):
# vector * scalar
if isinstance(other, (int, float)):
return Vector(other * self.x, other * self.y)
return NotImplemented
def __rmul__(self, other):
if isinstance(other, (int, float)):
return self.__mul__(other)
if isinstance(other, Matrix):
return Vector.matrix_premultiply(other, self)
return NotImplemented
def __truediv__(self, other):
# vector / scalar
if isinstance(other, (int, float)):
return Vector(self.x / other, self.y / other)
else:
return NotImplemented
def __floordiv__(self, other):
# vector / scalar
if isinstance(other, (int, float)):
return Vector(self.x // other, self.y // other)
else:
return NotImplemented
@property
def x(self):
"""
Read-only property returns x component of vector.
"""
return self.coords[0]
@property
def y(self):
"""
Read-only property returns y component of vector.
"""
return self.coords[1]
@property
def length(self):
"""
Read-only property returns length of vector.
"""
return math.sqrt(self.x ** 2 + self.y ** 2)
@property
def angle(self):
"""
Read-only property returns angle of vector.
"""
angle = math.atan2(self.y, self.x)
return angle
@property
def unit(self):
"""
Read-only property returns a unit vector with the same angle as this vector
"""
return self / self.length
# String representation
def __repr__(self):
return "Vector({0}, {1})".format(self.x, self.y)
def __str__(self):
return repr(self)
class Vector3:
"""
Class to represent a 3-vector including most of its common operations
"""
def __init__(self, *args):
"""
Can either accept 3 numbers, or a tuple containing 3 numerical elements.
Args:
args: various - see above
Returns:
Self
"""
if len(args) == 1 and hasattr(args[0], "__iter__") and len(args[0]) == 3:
self.coords = tuple(args[0])
elif (
len(args) == 3
and isinstance(args[0], (int, float))
and isinstance(args[1], (int, float))
and isinstance(args[2], (int, float))
):
self.coords = tuple(args)
else:
raise ValueError("Vector3 requires a sequence of length 3, or 3 numbers")
def lerp(self, other, factor):
"""
Interpolate between this vector and other.
The `factor` parameter works like this:
* 0 - result is self
* 1 - result is other
* 0 to 1 - result between self and other
* > 1 - result extensds beyond other
* < 0 - result extends backwards before other
Args:
other: Vector3 - the other vector
factor: number - The interpolation amount.
Returns:
New rotated vector
"""
return Vector3(
(1 - factor) * self.x + factor * other.x,
(1 - factor) * self.y + factor * other.y,
(1 - factor) * self.z + factor * other.z,
)
def __iter__(self):
return iter(self.coords)
def __len__(self):
return len(self.coords)
def __getitem__(self, index):
return self.coords[index]
def __eq__(self, other):
return isclose(self.x, other.x) and isclose(self.y, other.y)
def __neg__(self):
return self * -1
def __add__(self, other):
return Vector3(self.x + other.x, self.y + other.y, self.z + other.z)
def __sub__(self, other):
# add the negative of `other`
return self + (-other)
def __mul__(self, other):
# vector * scalar
if isinstance(other, (int, float)):
return Vector3(other * self.x, other * self.y, other * self.z)
return NotImplemented
def __rmul__(self, other):
if isinstance(other, (int, float)):
return self.__mul__(other)
return NotImplemented
def __truediv__(self, other):
# vector / scalar
if isinstance(other, (int, float)):
return Vector3(self.x / other, self.y / other, self.z / other)
else:
return NotImplemented
def __floordiv__(self, other):
# vector / scalar
if isinstance(other, (int, float)):
return Vector3(self.x // other, self.y // other, self.z // other)
else:
return NotImplemented
@property
def x(self):
"""
Read-only property returns x component of vector.
"""
return self.coords[0]
@property
def y(self):
"""
Read-only property returns y component of vector.
"""
return self.coords[1]
@property
def z(self):
"""
Read-only property returns z component of vector.
"""
return self.coords[2]
# String representation
def __repr__(self):
return "Vector3({0}, {1}, {2})".format(self.x, self.y, self.z)
def __str__(self):
return repr(self)
Functions
def isclose(a, b, rel_tol=1e-09, abs_tol=1e-12)
-
Check if two values a and b are equal to within a given tolerance
Args
a
- number - First value
b
- number - Second value
rel_tol
- number - Tolerance as a fraction of the absolute value of a or b (whichever is largest)
abs_tol
- number - Tolerance as an absolute value
Returns
True if the numbers are close, false otherwise.
Expand source code
def isclose(a, b, rel_tol=1e-09, abs_tol=1e-12): """ Check if two values a and b are equal to within a given tolerance Args: a: number - First value b: number - Second value rel_tol: number - Tolerance as a fraction of the absolute value of a or b (whichever is largest) abs_tol: number - Tolerance as an absolute value Returns: True if the numbers are close, false otherwise. """ return abs(a - b) <= max(rel_tol * max(abs(a), abs(b)), abs_tol)
Classes
class Matrix (xx, xy, xt, yx, yy, yt)
-
Class to represent a 2D transform matrix:
| xx xy xt | | yx yy yt |
Expand source code
class Matrix(): """ Class to represent a 2D transform matrix: ``` | xx xy xt | | yx yy yt | ``` """ @staticmethod def unit(): """ Create a unit matrix Returns: The unit matrix. """ return Matrix(1, 0, 0, 0, 1, 0) @staticmethod def scale(scale_x, scale_y=None): """ Create a scaling matrix Args: scale_x: Scale factor in x direction scale_y: Scale factor in y direction, defaults to scale_x Returns: New matrix """ if scale_y is None: scale_y = scale_x return Matrix(scale_x, 0, 0, 0, scale_y, 0) @staticmethod def translate(x, y): """ Create a translation matrix Args: x: Translation in x direction y: Translation in y direction Returns: New matrix """ return Matrix(1, 0, x, 0, 1, y) @staticmethod def rotate(angle): """ Create a rotation matrix Args: angle: Angle in radians, measured counterclockwise from positive x direction Returns: New matrix """ c = math.cos(angle) s = math.sin(angle) return Matrix(c, -s, 0, s, c, 0) @staticmethod def multiply(p, q): """ Multiply two matrices Args: a: First matrix b: Second matrix Returns: New matrix """ a = p[0] * q[0] + p[1] * q[3] b = p[0] * q[1] + p[1] * q[4] c = p[0] * q[2] + p[1] * q[5] + p[2] d = p[3] * q[0] + p[4] * q[3] e = p[3] * q[1] + p[4] * q[4] f = p[3] * q[2] + p[4] * q[5] + p[5] return Matrix(a, b, c, d, e, f) def __init__(self, xx, xy, xt, yx, yy, yt): self.matrix = (xx, xy, xt, yx, yy, yt) def __iter__(self): return iter(self.matrix) def __len__(self): return len(self.matrix) def __getitem__(self, index): return self.matrix[index] def __eq__(self, other): return all([isclose(a, b) for a, b in zip(self, other)]) def __neg__(self): return self * -1 def __add__(self, other): return Matrix(*[a + b for a, b in zip(self, other)]) def __sub__(self, other): # add the negative of `other` return self + (-other) def __mul__(self, other): # matrix * scalar if isinstance(other, (int, float)): return Matrix(*[other * a for a in self]) if isinstance(other, Matrix): return Matrix.multiply(self, other) return NotImplemented def __rmul__(self, other): return self.__mul__(other) def __truediv__(self, other): # matrix / scalar if isinstance(other, (int, float)): return Matrix(*[a / other for a in self]) else: return NotImplemented def __floordiv__(self, other): # matrix // scalar if isinstance(other, (int, float)): return Matrix(*[a // other for a in self]) else: return NotImplemented def __repr__(self): return "Matrix({0}, {1}, {2}, {3}, {4}, {5})".format(*self.matrix) def __str__(self): return repr(self)
Static methods
def multiply(p, q)
-
Multiply two matrices
Args
a
- First matrix
b
- Second matrix
Returns
New matrix
Expand source code
@staticmethod def multiply(p, q): """ Multiply two matrices Args: a: First matrix b: Second matrix Returns: New matrix """ a = p[0] * q[0] + p[1] * q[3] b = p[0] * q[1] + p[1] * q[4] c = p[0] * q[2] + p[1] * q[5] + p[2] d = p[3] * q[0] + p[4] * q[3] e = p[3] * q[1] + p[4] * q[4] f = p[3] * q[2] + p[4] * q[5] + p[5] return Matrix(a, b, c, d, e, f)
def rotate(angle)
-
Create a rotation matrix
Args
angle
- Angle in radians, measured counterclockwise from positive x direction
Returns
New matrix
Expand source code
@staticmethod def rotate(angle): """ Create a rotation matrix Args: angle: Angle in radians, measured counterclockwise from positive x direction Returns: New matrix """ c = math.cos(angle) s = math.sin(angle) return Matrix(c, -s, 0, s, c, 0)
def scale(scale_x, scale_y=None)
-
Create a scaling matrix
Args
scale_x
- Scale factor in x direction
scale_y
- Scale factor in y direction, defaults to scale_x
Returns
New matrix
Expand source code
@staticmethod def scale(scale_x, scale_y=None): """ Create a scaling matrix Args: scale_x: Scale factor in x direction scale_y: Scale factor in y direction, defaults to scale_x Returns: New matrix """ if scale_y is None: scale_y = scale_x return Matrix(scale_x, 0, 0, 0, scale_y, 0)
def translate(x, y)
-
Create a translation matrix
Args
x
- Translation in x direction
y
- Translation in y direction
Returns
New matrix
Expand source code
@staticmethod def translate(x, y): """ Create a translation matrix Args: x: Translation in x direction y: Translation in y direction Returns: New matrix """ return Matrix(1, 0, x, 0, 1, y)
def unit()
-
Create a unit matrix
Returns
The unit matrix.
Expand source code
@staticmethod def unit(): """ Create a unit matrix Returns: The unit matrix. """ return Matrix(1, 0, 0, 0, 1, 0)
class Vector (*args)
-
Class to represent a 2-vector including most of its common operations This is based on easy_vector https://github.com/DariusMontez/easy_vector The main changes are to make the object immutable, and measuring angles in radians rather than degrees
Can either accept 2 number, or a tuple containing 2 numerical elements.
Args
args
- various - see above
Returns
Self
Expand source code
class Vector(): """ Class to represent a 2-vector including most of its common operations This is based on easy_vector https://github.com/DariusMontez/easy_vector The main changes are to make the object immutable, and measuring angles in radians rather than degrees """ @staticmethod def polar(length, angle): """ Create a vector based on a length and angle Args: length: Length of vector angle: Angle in radians, measured counterclockwise from positive x direction Returns: New vector """ x = length * math.cos(angle) y = length * math.sin(angle) return Vector(x, y) @staticmethod def matrix_premultiply(m, v): """ Multiply a matrix (first) and a vector (second) Args: m: matrix v: vector Returns: New vector """ a = m[0] * v[0] + m[1] * v[1] + m[2] b = m[3] * v[0] + m[4] * v[1] + m[5] return Vector(a, b) def __init__(self, *args): """ Can either accept 2 number, or a tuple containing 2 numerical elements. Args: args: various - see above Returns: Self """ if len(args) == 1 and hasattr(args[0], "__iter__") and len(args[0]) == 2: self.coords = tuple(args[0]) elif len(args) == 2 and isinstance(args[0], (int, float)) and isinstance(args[1], (int, float)): self.coords = tuple(args) else: raise ValueError("Vector requires a sequence of length 2, or 2 numbers") def transform(self, m): """ Transform this vector by a matrix. The vector is pre-multiplied by the matrix Args: m: matrix Returns: New transformed vector """ return m * self def scale(self, scale_x, scale_y=None): """ Scale this vector by a factor. Args: scale_x: scale factor in x direction. scale_y: scale factor in y direction. If this is None, scale by scale_x in both directions. Returns: New scaled vector """ return Matrix.scale(scale_x, scale_y) * self def translate(self, x, y): """ Translate this vector by (x, y), Args: x: translation amount in x direction. y: translation amount in y direction. Returns: New translated vector """ return Matrix.translate(x, y) * self def rotate(self, angle): """ Rotate this vector by (x, y), Args: x: rotation amount in x direction. y: rotation amount in y direction. Returns: New rotated vector """ return Matrix.rotate(angle) * self def lerp(self, other, factor): """ Interpolate between this vector and other. The `factor` parameter works like this: * 0 - result is self * 1 - result is other * 0 to 1 - result between self and other * > 1 - result extensds beyond other * < 0 - result extends backwards before other Args: other: Vector - the other vector factor: number - The interpolation amount. Returns: New rotated vector """ return Vector((1 - factor) * self.x + factor * other.x, (1 - factor) * self.y + factor * other.y) def __iter__(self): return iter(self.coords) def __len__(self): return len(self.coords) def __getitem__(self, index): return self.coords[index] def __eq__(self, other): return isclose(self.x, other.x) and isclose(self.y, other.y) def __neg__(self): return self * -1 def __add__(self, other): return Vector(self.x + other.x, self.y + other.y) def __sub__(self, other): # add the negative of `other` return self + (-other) def __mul__(self, other): # vector * scalar if isinstance(other, (int, float)): return Vector(other * self.x, other * self.y) return NotImplemented def __rmul__(self, other): if isinstance(other, (int, float)): return self.__mul__(other) if isinstance(other, Matrix): return Vector.matrix_premultiply(other, self) return NotImplemented def __truediv__(self, other): # vector / scalar if isinstance(other, (int, float)): return Vector(self.x / other, self.y / other) else: return NotImplemented def __floordiv__(self, other): # vector / scalar if isinstance(other, (int, float)): return Vector(self.x // other, self.y // other) else: return NotImplemented @property def x(self): """ Read-only property returns x component of vector. """ return self.coords[0] @property def y(self): """ Read-only property returns y component of vector. """ return self.coords[1] @property def length(self): """ Read-only property returns length of vector. """ return math.sqrt(self.x ** 2 + self.y ** 2) @property def angle(self): """ Read-only property returns angle of vector. """ angle = math.atan2(self.y, self.x) return angle @property def unit(self): """ Read-only property returns a unit vector with the same angle as this vector """ return self / self.length # String representation def __repr__(self): return "Vector({0}, {1})".format(self.x, self.y) def __str__(self): return repr(self)
Static methods
def matrix_premultiply(m, v)
-
Multiply a matrix (first) and a vector (second)
Args
m
- matrix
v
- vector
Returns
New vector
Expand source code
@staticmethod def matrix_premultiply(m, v): """ Multiply a matrix (first) and a vector (second) Args: m: matrix v: vector Returns: New vector """ a = m[0] * v[0] + m[1] * v[1] + m[2] b = m[3] * v[0] + m[4] * v[1] + m[5] return Vector(a, b)
def polar(length, angle)
-
Create a vector based on a length and angle
Args
length
- Length of vector
angle
- Angle in radians, measured counterclockwise from positive x direction
Returns
New vector
Expand source code
@staticmethod def polar(length, angle): """ Create a vector based on a length and angle Args: length: Length of vector angle: Angle in radians, measured counterclockwise from positive x direction Returns: New vector """ x = length * math.cos(angle) y = length * math.sin(angle) return Vector(x, y)
Instance variables
var angle
-
Read-only property returns angle of vector.
Expand source code
@property def angle(self): """ Read-only property returns angle of vector. """ angle = math.atan2(self.y, self.x) return angle
var length
-
Read-only property returns length of vector.
Expand source code
@property def length(self): """ Read-only property returns length of vector. """ return math.sqrt(self.x ** 2 + self.y ** 2)
var unit
-
Read-only property returns a unit vector with the same angle as this vector
Expand source code
@property def unit(self): """ Read-only property returns a unit vector with the same angle as this vector """ return self / self.length
var x
-
Read-only property returns x component of vector.
Expand source code
@property def x(self): """ Read-only property returns x component of vector. """ return self.coords[0]
var y
-
Read-only property returns y component of vector.
Expand source code
@property def y(self): """ Read-only property returns y component of vector. """ return self.coords[1]
Methods
def lerp(self, other, factor)
-
Interpolate between this vector and other.
The
factor
parameter works like this:- 0 - result is self
- 1 - result is other
- 0 to 1 - result between self and other
-
1 - result extensds beyond other
- < 0 - result extends backwards before other
Args
other
- Vector - the other vector
factor
- number - The interpolation amount.
Returns
New rotated vector
Expand source code
def lerp(self, other, factor): """ Interpolate between this vector and other. The `factor` parameter works like this: * 0 - result is self * 1 - result is other * 0 to 1 - result between self and other * > 1 - result extensds beyond other * < 0 - result extends backwards before other Args: other: Vector - the other vector factor: number - The interpolation amount. Returns: New rotated vector """ return Vector((1 - factor) * self.x + factor * other.x, (1 - factor) * self.y + factor * other.y)
def rotate(self, angle)
-
Rotate this vector by (x, y),
Args
x
- rotation amount in x direction.
y
- rotation amount in y direction.
Returns
New rotated vector
Expand source code
def rotate(self, angle): """ Rotate this vector by (x, y), Args: x: rotation amount in x direction. y: rotation amount in y direction. Returns: New rotated vector """ return Matrix.rotate(angle) * self
def scale(self, scale_x, scale_y=None)
-
Scale this vector by a factor.
Args
scale_x
- scale factor in x direction.
scale_y
- scale factor in y direction. If this is None, scale by scale_x in both directions.
Returns
New scaled vector
Expand source code
def scale(self, scale_x, scale_y=None): """ Scale this vector by a factor. Args: scale_x: scale factor in x direction. scale_y: scale factor in y direction. If this is None, scale by scale_x in both directions. Returns: New scaled vector """ return Matrix.scale(scale_x, scale_y) * self
def transform(self, m)
-
Transform this vector by a matrix. The vector is pre-multiplied by the matrix
Args
m
- matrix
Returns
New transformed vector
Expand source code
def transform(self, m): """ Transform this vector by a matrix. The vector is pre-multiplied by the matrix Args: m: matrix Returns: New transformed vector """ return m * self
def translate(self, x, y)
-
Translate this vector by (x, y),
Args
x
- translation amount in x direction.
y
- translation amount in y direction.
Returns
New translated vector
Expand source code
def translate(self, x, y): """ Translate this vector by (x, y), Args: x: translation amount in x direction. y: translation amount in y direction. Returns: New translated vector """ return Matrix.translate(x, y) * self
class Vector3 (*args)
-
Class to represent a 3-vector including most of its common operations
Can either accept 3 numbers, or a tuple containing 3 numerical elements.
Args
args
- various - see above
Returns
Self
Expand source code
class Vector3: """ Class to represent a 3-vector including most of its common operations """ def __init__(self, *args): """ Can either accept 3 numbers, or a tuple containing 3 numerical elements. Args: args: various - see above Returns: Self """ if len(args) == 1 and hasattr(args[0], "__iter__") and len(args[0]) == 3: self.coords = tuple(args[0]) elif ( len(args) == 3 and isinstance(args[0], (int, float)) and isinstance(args[1], (int, float)) and isinstance(args[2], (int, float)) ): self.coords = tuple(args) else: raise ValueError("Vector3 requires a sequence of length 3, or 3 numbers") def lerp(self, other, factor): """ Interpolate between this vector and other. The `factor` parameter works like this: * 0 - result is self * 1 - result is other * 0 to 1 - result between self and other * > 1 - result extensds beyond other * < 0 - result extends backwards before other Args: other: Vector3 - the other vector factor: number - The interpolation amount. Returns: New rotated vector """ return Vector3( (1 - factor) * self.x + factor * other.x, (1 - factor) * self.y + factor * other.y, (1 - factor) * self.z + factor * other.z, ) def __iter__(self): return iter(self.coords) def __len__(self): return len(self.coords) def __getitem__(self, index): return self.coords[index] def __eq__(self, other): return isclose(self.x, other.x) and isclose(self.y, other.y) def __neg__(self): return self * -1 def __add__(self, other): return Vector3(self.x + other.x, self.y + other.y, self.z + other.z) def __sub__(self, other): # add the negative of `other` return self + (-other) def __mul__(self, other): # vector * scalar if isinstance(other, (int, float)): return Vector3(other * self.x, other * self.y, other * self.z) return NotImplemented def __rmul__(self, other): if isinstance(other, (int, float)): return self.__mul__(other) return NotImplemented def __truediv__(self, other): # vector / scalar if isinstance(other, (int, float)): return Vector3(self.x / other, self.y / other, self.z / other) else: return NotImplemented def __floordiv__(self, other): # vector / scalar if isinstance(other, (int, float)): return Vector3(self.x // other, self.y // other, self.z // other) else: return NotImplemented @property def x(self): """ Read-only property returns x component of vector. """ return self.coords[0] @property def y(self): """ Read-only property returns y component of vector. """ return self.coords[1] @property def z(self): """ Read-only property returns z component of vector. """ return self.coords[2] # String representation def __repr__(self): return "Vector3({0}, {1}, {2})".format(self.x, self.y, self.z) def __str__(self): return repr(self)
Instance variables
var x
-
Read-only property returns x component of vector.
Expand source code
@property def x(self): """ Read-only property returns x component of vector. """ return self.coords[0]
var y
-
Read-only property returns y component of vector.
Expand source code
@property def y(self): """ Read-only property returns y component of vector. """ return self.coords[1]
var z
-
Read-only property returns z component of vector.
Expand source code
@property def z(self): """ Read-only property returns z component of vector. """ return self.coords[2]
Methods
def lerp(self, other, factor)
-
Interpolate between this vector and other.
The
factor
parameter works like this:- 0 - result is self
- 1 - result is other
- 0 to 1 - result between self and other
-
1 - result extensds beyond other
- < 0 - result extends backwards before other
Args
other
- Vector3 - the other vector
factor
- number - The interpolation amount.
Returns
New rotated vector
Expand source code
def lerp(self, other, factor): """ Interpolate between this vector and other. The `factor` parameter works like this: * 0 - result is self * 1 - result is other * 0 to 1 - result between self and other * > 1 - result extensds beyond other * < 0 - result extends backwards before other Args: other: Vector3 - the other vector factor: number - The interpolation amount. Returns: New rotated vector """ return Vector3( (1 - factor) * self.x + factor * other.x, (1 - factor) * self.y + factor * other.y, (1 - factor) * self.z + factor * other.z, )