import vendor modules only once during import change text color for the method cad_wireframe in geometry_plot_pylab and add hide key
1097 lines
27 KiB
Python
1097 lines
27 KiB
Python
#!/usr/bin/env python
|
|
# -*- coding: utf-8 -*-
|
|
"""Mathematical functions and objects.
|
|
|
|
:Date: 2019-12-12
|
|
|
|
.. module:: mathematics
|
|
:platform: *nix, Windows
|
|
:synopsis: Mathematical functions and objects.
|
|
|
|
.. moduleauthor:: Daniel Weschke <daniel.weschke@directbox.de>
|
|
"""
|
|
import math
|
|
from math import gcd
|
|
|
|
def lcm(a, b):
|
|
"""Compute the lowest common multiple of a and b"""
|
|
return a/gcd(a, b)*b
|
|
|
|
|
|
class vector(list):
|
|
"""Use/create vector like lists
|
|
|
|
* size, length of a vector use len(a)
|
|
* absolute, magntude, norm of a vector use abs(a), see
|
|
:meth:`__abs__`
|
|
* dot product use a * b, see :meth:`__mul__` and :meth:`__rmul__`
|
|
* outer product use a @ b, see :meth:`__matmul__`
|
|
|
|
:__iter__:
|
|
|
|
>>> [i*2 for i in vector([1, 2, 3])]
|
|
[2, 4, 6]
|
|
|
|
:__setitem__:
|
|
|
|
>>> v = vector([1, 2, 3, 4, 5])
|
|
>>> v[3:5] = [1, 2]
|
|
>>> print(v)
|
|
[1, 2, 3, 1, 2]
|
|
>>> isinstance(v, vector)
|
|
True
|
|
|
|
:__eq__(a, b):
|
|
|
|
>>> vector([1, 2, 3, 1, 2]) == vector([1, 2, 3, 1, 2])
|
|
True
|
|
>>> vector([1, 2, 3, 1, 2]) == vector([1, 2, 3, 2, 1])
|
|
False
|
|
|
|
:__ne__(a, b):
|
|
|
|
>>> vector([1, 2, 3, 1, 2]) != vector([1, 2, 3, 1, 2])
|
|
False
|
|
>>> vector([1, 2, 3, 1, 2]) != vector([1, 2, 3, 2, 1])
|
|
True
|
|
|
|
:__contains__:
|
|
|
|
>>> 2 in vector([1, 2, 3])
|
|
True
|
|
|
|
:__isub__:
|
|
|
|
>>> v = vector([1, 2, 3])
|
|
>>> v -= vector([3, 3, 3])
|
|
>>> print(v)
|
|
[-2, -1, 0]
|
|
|
|
:__imul__:
|
|
|
|
>>> v = vector([1, 2, 3])
|
|
>>> v *= vector([3, 3, 3])
|
|
>>> print(v)
|
|
18
|
|
|
|
:__imatmul__:
|
|
|
|
>>> m = vector([1, 2, 3])
|
|
>>> m *= vector([3, 3, 3])
|
|
>>> print(v)
|
|
[[3, 3, 3], [6, 6, 6], [9, 9, 9]]
|
|
"""
|
|
|
|
def __getitem__(self, index):
|
|
"""
|
|
For index return value, for range return new vector object.
|
|
|
|
:Example:
|
|
|
|
>>> v = vector([1, 2, 3, 4, 5])
|
|
>>> v[1:3]
|
|
[2, 3]
|
|
>>> v = vector([1, 2, 3, 4, 5])
|
|
>>> v[3]
|
|
4
|
|
"""
|
|
# use the list.__getitem__ method and convert result to vector
|
|
item = super().__getitem__(index)
|
|
return vector(item) if isinstance(item, list) else item
|
|
|
|
def __pos__(self):
|
|
"""\+ a (new object)
|
|
|
|
.. math::
|
|
\mathbf{b} = +\mathbf{a}
|
|
"""
|
|
return vector([*self])
|
|
|
|
def __neg__(self):
|
|
"""\- a (new object)
|
|
|
|
.. math::
|
|
\mathbf{b} = -\mathbf{a}
|
|
"""
|
|
return vector([-i for i in self])
|
|
|
|
def __add__(self, other):
|
|
"""a + b (new object)
|
|
|
|
.. math::
|
|
\mathbf{c} = \mathbf{a} + \mathbf{b}
|
|
|
|
:Example:
|
|
|
|
>>> v = vector([1, 2, 3]) + vector([1, 2, 3])
|
|
>>> print(v)
|
|
[2, 4, 6]
|
|
"""
|
|
return vector([i+j for i, j in zip(self, other)])
|
|
|
|
# overwrite because [1, 2] + [5, 8] = [1, 2, 5, 8]
|
|
def __iadd__(self, other):
|
|
"""a += b (new object)
|
|
|
|
:Example:
|
|
|
|
>>> v = vector([1, 2, 3])
|
|
>>> v += vector([3, 3, 3])
|
|
>>> print(v)
|
|
[4, 5, 6]
|
|
"""
|
|
return vector([i+j for i, j in zip(self, other)])
|
|
|
|
def __sub__(self, other):
|
|
"""a - b (new object)
|
|
|
|
.. math::
|
|
\mathbf{c} = \mathbf{a} - \mathbf{b}
|
|
|
|
:Example:
|
|
|
|
>>> v = vector([1, 2, 3]) - vector([1, 2, 3])
|
|
>>> print(v)
|
|
[0, 0, 0]
|
|
"""
|
|
return vector([i-j for i, j in zip(self, other)])
|
|
|
|
def __mul__(self, other):
|
|
r"""Scalar multiplication, dot product (inner product) or
|
|
vector-matrix multiplication. (new object)
|
|
|
|
:type other: scalar, vector (or 1d list), matrix (or 2d list)
|
|
|
|
.. math::
|
|
\mathbf{c} &= \mathbf{a} \cdot b \\
|
|
\mathbf{c} &= \mathbf{a} \cdot \mathbf{b} \\
|
|
\mathbf{c} &= \mathbf{a} \cdot \mathbf{B}
|
|
|
|
.. note::
|
|
No size checking will be conducted, therefore no exceptions
|
|
for wrong usage (result will be nonsense).
|
|
|
|
:Example:
|
|
|
|
>>> v = vector([1, 2, 3, 4, 5]) * 3
|
|
>>> print(v)
|
|
[3, 6, 9, 12, 15]
|
|
>>> v = vector([1, 2, 3, 4, 5]) * 3.
|
|
>>> print(v)
|
|
[3.0, 6.0, 9.0, 12.0, 15.0]
|
|
>>> s = vector([1, 2, 3]) * vector([1, 2, 3])
|
|
>>> print(s)
|
|
14
|
|
|
|
.. seealso::
|
|
:meth:`__rmul__`
|
|
"""
|
|
try: # vector * vector
|
|
return sum([i*j for i, j in zip(self, other)])
|
|
except:
|
|
try: # vector * matrix
|
|
return vector([sum(c*d for c, d in zip(self, b_col)) for
|
|
b_col in zip(*other)])
|
|
except: # vector * scalar
|
|
return vector([i*other for i in self])
|
|
|
|
def __rmul__(self, other):
|
|
r"""Scalar multiplication, dot product (inner product) or
|
|
matrix-vector multiplication. (new object)
|
|
|
|
:type other: scalar (or 1d list and 2d list)
|
|
|
|
.. math::
|
|
\mathbf{c} &= a \cdot \mathbf{b} \\
|
|
\mathbf{c} &= \mathbf{a} \cdot \mathbf{b} \\
|
|
\mathbf{c} &= \mathbf{A} \cdot \mathbf{b}
|
|
|
|
.. note::
|
|
No size checking will be conducted, therefore no exceptions
|
|
for wrong usage (result will be nonsense).
|
|
|
|
:Example:
|
|
|
|
>>> v = 3 * vector([1, 2, 3, 4, 5])
|
|
>>> print(v)
|
|
[3, 6, 9, 12, 15]
|
|
>>> v = 3. * vector([1, 2, 3, 4, 5])
|
|
>>> print(v)
|
|
[3.0, 6.0, 9.0, 12.0, 15.0]
|
|
|
|
.. seealso::
|
|
:meth:`__mul__` and :meth:`matrix.__mul__` for matrix * vector
|
|
"""
|
|
try: # 2d list * vector (matrix * vector see matrix.__mul__)
|
|
return vector([sum(c*d for c, d in zip(a_row, self)) for
|
|
a_row in other])
|
|
except: # scalar * vector
|
|
return self*other
|
|
|
|
def __matmul__(self, other):
|
|
r"""Outer product a @ b (new object)
|
|
|
|
.. math::
|
|
\mathbf{c} = \mathbf{a} \otimes \mathbf{b}
|
|
=
|
|
\begin{pmatrix}
|
|
a_{1}\\
|
|
a_{2}\\
|
|
a_{3}
|
|
\end{pmatrix}
|
|
\otimes
|
|
\begin{pmatrix}
|
|
b_{1}\\
|
|
b_{2}\\
|
|
b_{3}
|
|
\end{pmatrix}
|
|
=
|
|
\begin{pmatrix}
|
|
a_{1}b_{1}&a_{1}b_{2}&a_{1}b_{3}\\
|
|
a_{2}b_{1}&a_{2}b_{2}&a_{2}b_{3}\\
|
|
a_{3}b_{1}&a_{3}b_{2}&a_{3}b_{3}
|
|
\end{pmatrix}
|
|
|
|
|
|
:Example:
|
|
|
|
>>> m = vector([1, 2, 3]) @ vector([1, 2, 4])
|
|
>>> print(m)
|
|
[[1, 2, 4], [2, 4, 8], [3, 6, 12]]
|
|
"""
|
|
try: # vector * vector
|
|
return matrix([[i*j for j in other] for i in self])
|
|
except: # vector * number
|
|
return self*other
|
|
|
|
def __abs__(self):
|
|
r"""Magnitude / norm of a vector
|
|
|
|
.. math::
|
|
b = |\mathbf{a}| = \sqrt{\sum a_i^2} =
|
|
\sqrt{\mathbf{a} \cdot \mathbf{a}}
|
|
|
|
:Example:
|
|
|
|
>>> v = vector([3, 4])
|
|
>>> abs(v)
|
|
5.0
|
|
"""
|
|
return math.sqrt(self * self)
|
|
|
|
def __lt__(self, other):
|
|
"""Test if this object is lower (smaller) than the other object.
|
|
|
|
.. math::
|
|
|\mathbf{a}| \lt |\mathbf{b}|
|
|
|
|
:Example:
|
|
|
|
>>> vector([3, 2, 1]) < vector([1, 2, 3])
|
|
False
|
|
>>> vector([3, 2, 1]) < vector([1, 2, 4])
|
|
True
|
|
"""
|
|
return abs(self) < abs(other)
|
|
|
|
def __le__(self, other):
|
|
"""Test if this object is lower (smaller) than or equal the
|
|
other object.
|
|
|
|
.. math::
|
|
|\mathbf{a}| \le |\mathbf{b}|
|
|
|
|
:Example:
|
|
|
|
>>> vector([3, 2, 1]) <= vector([1, 2, 3])
|
|
True
|
|
>>> vector([3, 2, 1]) <= vector([1, 2, 2])
|
|
False
|
|
"""
|
|
return abs(self) <= abs(other)
|
|
|
|
def __gt__(self, other):
|
|
"""Test if this object is greater (larger) than the other
|
|
object.
|
|
|
|
.. math::
|
|
|\mathbf{a}| \gt |\mathbf{b}|
|
|
|
|
:Example:
|
|
|
|
>>> vector([1, 2, 3]) > vector([3, 2, 1])
|
|
False
|
|
>>> vector([1, 2, 3]) > vector([2, 2, 1])
|
|
True
|
|
"""
|
|
return abs(self) > abs(other)
|
|
|
|
def __ge__(self, other):
|
|
"""Test if this object is greater (larger) than or equal the
|
|
other object.
|
|
|
|
.. math::
|
|
|\mathbf{a}| \ge |\mathbf{b}|
|
|
|
|
:Example:
|
|
|
|
>>> vector([1, 2, 3]) >= vector([3, 2, 1])
|
|
True
|
|
>>> vector([1, 2, 3]) >= vector([4, 2, 1])
|
|
False
|
|
"""
|
|
return abs(self) >= abs(other)
|
|
|
|
def __str__(self):
|
|
return str([*self])
|
|
|
|
def __repr__(self):
|
|
return "vector(" + str(self) + ")"
|
|
|
|
@staticmethod
|
|
def full(length, fill_value):
|
|
"""Returns a vector of length m filled with v.
|
|
|
|
:param length: length of the vector, e. g. 3
|
|
:type length: int
|
|
:param fill_value: fill value
|
|
:Type fill_value: scalar
|
|
|
|
:Example:
|
|
|
|
>>> v = vector.full(3, 7)
|
|
>>> print(v)
|
|
[7, 7, 7]
|
|
"""
|
|
return vector([fill_value for i in range(length)])
|
|
|
|
@staticmethod
|
|
def zeros(length):
|
|
"""Returns a zero vector of length m filled with zeros.
|
|
|
|
:param length: length of the vector, e. g. 3
|
|
:type length: int
|
|
|
|
:Example:
|
|
|
|
>>> v = vector.zeros(3)
|
|
>>> print(v)
|
|
[0.0, 0.0, 0.0]
|
|
"""
|
|
return vector.full(length, 0.)
|
|
|
|
@staticmethod
|
|
def ones(length):
|
|
"""Returns a vector of length m filled with ones.
|
|
|
|
:param length: lhape of the vector, e. g. 3
|
|
:type length: int
|
|
|
|
:Example:
|
|
|
|
>>> v = vector.ones(3)
|
|
>>> print(v)
|
|
[1.0, 1.0, 1.0]
|
|
"""
|
|
return vector.full(length, 1.)
|
|
|
|
@staticmethod
|
|
def random(length, lmin=0.0, lmax=1.0):
|
|
"""Returns a random vector of length n filled with random
|
|
numbers.
|
|
|
|
:param length: lhape of the vector, e. g. 3
|
|
:type length: int
|
|
|
|
:Example:
|
|
|
|
>>> v = vector.random(3)
|
|
>>> print(v)
|
|
[0.9172905912930438, 0.8908124278322492, 0.5256002790725927]
|
|
>>> v = vector.random(3, 1, 2)
|
|
>>> print(v)
|
|
[1.2563665665080803, 1.9270454509964547, 1.2381672401270487]
|
|
"""
|
|
import random
|
|
dl = lmax-lmin
|
|
return vector([dl*random.random()+lmin for i in range(length)])
|
|
|
|
@staticmethod
|
|
def normalized(a):
|
|
r"""Normalize a vector (i. e. the vector has a length of 1)
|
|
|
|
:type a: vector
|
|
|
|
.. math::
|
|
\mathbf{\hat{a}} = \frac{\mathbf{a}}{|\mathbf{a}|}
|
|
|
|
.. seealso::
|
|
:meth:`__abs__` for a norm (magnitude) of a vector
|
|
"""
|
|
a_mag = abs(a)
|
|
return vector([i/a_mag for i in a])
|
|
|
|
@staticmethod
|
|
def ang(a, b):
|
|
x = a * b / (abs(a) * abs(b))
|
|
# decimal floating-point numbers are only approximated by the
|
|
# binary floating-point numbers actually stored in the machine.
|
|
# https://docs.python.org/3.8/tutorial/floatingpoint.html
|
|
xr15 = round(x, 15)
|
|
return math.acos(xr15) if -1 <= xr15 <= 1 else math.acos(x)
|
|
|
|
@staticmethod
|
|
def conjugate(a):
|
|
"""
|
|
New vector object
|
|
|
|
:type a: list
|
|
"""
|
|
return vector([i.conjugate() for i in a])
|
|
|
|
@staticmethod
|
|
def re(a):
|
|
"""Return the real parts of a complex vector
|
|
|
|
:type a: list
|
|
"""
|
|
return vector([i.real for i in a])
|
|
|
|
@staticmethod
|
|
def im(a):
|
|
"""Return the imaginary parts of a complex vector
|
|
|
|
:type a: list
|
|
"""
|
|
return vector([i.imag for i in a])
|
|
|
|
@staticmethod
|
|
def abs(a):
|
|
"""Return modulus parts of a complex vector
|
|
|
|
:type a: list
|
|
"""
|
|
return vector([abs(i) for i in a])
|
|
|
|
@staticmethod
|
|
def arg(a):
|
|
"""Return phase parts of a complex vector
|
|
|
|
:type a: list
|
|
"""
|
|
return vector([math.atan2(i.imag, i.real) for i in a])
|
|
|
|
@staticmethod
|
|
def cross(a, b):
|
|
r"""Cross product
|
|
|
|
:type a: list
|
|
:type b: list
|
|
|
|
c is orthogonal to both a and b.
|
|
The direction of c can be found with the right-hand rule.
|
|
|
|
.. math::
|
|
\mathbf{c} = \mathbf{a} \times \mathbf{b}
|
|
"""
|
|
return vector([a[1]*b[2] - a[2]*b[1],
|
|
a[2]*b[0] - a[0]*b[2],
|
|
a[0]*b[1] - a[1]*b[0]])
|
|
|
|
@staticmethod
|
|
def isclose(a, b, rel_tol=0.05, abs_tol=1e-8):
|
|
return all([math.isclose(i, j, rel_tol=rel_tol, abs_tol=abs_tol) for i, j in zip(a, b)])
|
|
|
|
def iscloseto(self, other):
|
|
return vector.isclose(self, other)
|
|
|
|
def normalize(self):
|
|
r"""Normalize a vector (i. e. the vector has a length of 1)
|
|
|
|
:type a: vector
|
|
|
|
.. math::
|
|
\mathbf{\hat{a}} = \frac{\mathbf{a}}{|\mathbf{a}|}
|
|
|
|
.. seealso::
|
|
:meth:`__abs__` for a norm (magnitude) of a vector
|
|
"""
|
|
self[:] = vector.normalized(self)
|
|
return self
|
|
|
|
def rotate_x(self, theta):
|
|
r"""Rotation about the x dirction.
|
|
|
|
.. math::
|
|
\begin{bmatrix}x' \\ y' \\ z' \\ h'\end{bmatrix} =
|
|
\begin{bmatrix}
|
|
1 & 0 & 0 & 0 \\
|
|
0 & \cos \theta & -\sin \theta & 0 \\
|
|
0 & \sin \theta & \cos \theta & 0 \\
|
|
0 & 0 & 0 & 1
|
|
\end{bmatrix}
|
|
\begin{bmatrix}x \\ y \\ z \\ h\end{bmatrix}
|
|
"""
|
|
self[:] = matrix.rx(theta) * self
|
|
return self
|
|
|
|
def rotate_y(self, theta):
|
|
r"""Rotation about the y dirction.
|
|
|
|
.. math::
|
|
\begin{bmatrix}x' \\ y' \\ z' \\ h'\end{bmatrix} =
|
|
\begin{bmatrix}
|
|
\cos \theta & 0 & \sin \theta & 0 \\
|
|
0 & 1 & 0 & 0 \\
|
|
-\sin \theta & 0 & \cos \theta & 0 \\
|
|
0 & 0 & 0 & 1
|
|
\end{bmatrix}
|
|
\begin{bmatrix}x \\ y \\ z \\ h\end{bmatrix}
|
|
"""
|
|
self[:] = matrix.ry(theta) * self
|
|
return self
|
|
|
|
def rotate_z(self, theta):
|
|
r"""Rotation about the z dirction.
|
|
|
|
.. math::
|
|
\begin{bmatrix}x' \\ y' \\ z' \\ h'\end{bmatrix} =
|
|
\begin{bmatrix}
|
|
\cos \theta & -\sin \theta & 0 & 0 \\
|
|
\sin \theta & \cos \theta & 0 & 0 \\
|
|
0 & 0 & 1 & 0 \\
|
|
0 & 0 & 0 & 1
|
|
\end{bmatrix}
|
|
\begin{bmatrix}x \\ y \\ z \\ h\end{bmatrix}
|
|
"""
|
|
self[:] = matrix.rz(theta) * self
|
|
return self
|
|
|
|
def translate(self, tx, ty, tz):
|
|
r"""Translation
|
|
|
|
.. math::
|
|
\begin{bmatrix}x' \\ y' \\ z' \\ h'\end{bmatrix} =
|
|
\begin{bmatrix}
|
|
1 & 0 & 0 & t_x \\
|
|
0 & 1 & 0 & t_y \\
|
|
0 & 0 & 1 & t_z \\
|
|
0 & 0 & 0 & 1
|
|
\end{bmatrix}
|
|
\begin{bmatrix}x \\ y \\ z \\ h\end{bmatrix}
|
|
"""
|
|
self[:] = matrix.t(tx, ty, tz) * self
|
|
return self
|
|
|
|
def scale(self, sx, sy=None, sz=None):
|
|
r"""Scaling
|
|
|
|
uniform scaling if sx=sy=sz=s.
|
|
Note that scaling happens around the origin, so objects not
|
|
centered at the origin will have their centers move. To avoid
|
|
this, either scale the object when it's located at the origin,
|
|
or perform a translation afterwards to move the object back to
|
|
where it should be.
|
|
|
|
.. math::
|
|
\begin{bmatrix}x' \\ y' \\ z' \\ h'\end{bmatrix} =
|
|
\begin{bmatrix}
|
|
s_x & 0 & 0 & 0 \\
|
|
0 & s_y & 0 & 0 \\
|
|
0 & 0 & s_z & 0 \\
|
|
0 & 0 & 0 & 1
|
|
\end{bmatrix}
|
|
\begin{bmatrix}x \\ y \\ z \\ h\end{bmatrix}
|
|
"""
|
|
# if not sy is not suitable because 0 is also false
|
|
if sy is None:
|
|
sy = sx
|
|
sz = sx
|
|
self[:] = matrix.s(sx, sy, sz) * self
|
|
return self
|
|
|
|
def ch_cs(self, cs):
|
|
r"""Transform this vector from its defined coordinate system to
|
|
a new coordinate system, defined by the given coordinate system
|
|
(u, v and w direction vectors).
|
|
|
|
.. math::
|
|
\begin{bmatrix}x' \\ y' \\ z' \\ h'\end{bmatrix} =
|
|
\begin{bmatrix}
|
|
u_x & u_y & u_z & 0 \\
|
|
v_x & v_y & v_z & 0 \\
|
|
w_x & w_y & w_z & 0 \\
|
|
0 & 0 & 0 & 1
|
|
\end{bmatrix}
|
|
\begin{bmatrix}x \\ y \\ z \\ h\end{bmatrix}
|
|
"""
|
|
self[:] = cs * self
|
|
return self
|
|
|
|
class matrix(list):
|
|
"""Use/create matrix like list of lists
|
|
"""
|
|
|
|
def __getitem__(self, index):
|
|
r"""
|
|
For index return value, for range return new vector object.
|
|
|
|
:Example:
|
|
|
|
>>> m = matrix([[1, 2, 3, 0], [4, 5, 6, 0], [7, 8, 9, 0], \
|
|
[0, 0, 0, 0]])
|
|
>>> print(m[:])
|
|
[[1, 2, 3, 0], [4, 5, 6, 0], [7, 8, 9, 0], [0, 0, 0, 0]]
|
|
>>> print(m[2])
|
|
[7, 8, 9, 0]
|
|
>>> print(m[2, :])
|
|
[7, 8, 9, 0]
|
|
>>> print(m[:, 2])
|
|
[3, 6, 9, 0]
|
|
>>> print(m[2, 2])
|
|
9
|
|
>>> print(m[:, 1:3])
|
|
[[2, 3], [5, 6], [8, 9], [0, 0]]
|
|
>>> print(m[0:2, 1:3])
|
|
[[2, 3], [5, 6]]
|
|
>>> print(m[::2, ::2])
|
|
[[1, 3], [7, 9]]
|
|
"""
|
|
# TODO single row or column = vector (1d list)?
|
|
# index: slice(stop), slice(start, stop[, step])
|
|
# for m[(1,3),:] -> index = ((1, 3), slice(None, None, None))
|
|
# use the list.__getitem__ method and convert result to matrix
|
|
#print(index)
|
|
try: # 2d slicing (tuple of sclices)
|
|
try: # range:range or range:single: [:, 2]
|
|
#print(1)
|
|
item = [row.__getitem__(index[1]) for
|
|
row in super().__getitem__(index[0])]
|
|
except: # single:range: [2, :], [2, 2]
|
|
#print(2)
|
|
item = super().__getitem__(index[0]).__getitem__(index[1])
|
|
except: # 1d slicing: [:], [2], [2][2]
|
|
#print(3)
|
|
item = super().__getitem__(index)
|
|
#print(item)
|
|
return matrix(item) if isinstance(item, list) and \
|
|
isinstance(item[0], list) else item
|
|
|
|
def __setitem__(self, index, value):
|
|
#print(index, value)
|
|
try: # [2, 2]
|
|
super().__getitem__(index[0]).__setitem__(index[1], value)
|
|
except: # [2], [2][2]
|
|
super().__setitem__(index, value)
|
|
#print(self)
|
|
|
|
def __mul__(self, other):
|
|
r"""Scalar multiplication, dot product (inner product) or
|
|
matrix-vector multiplication. (new object)
|
|
|
|
:type other: scalar, vector (or 1d list), matrix (or 2d list)
|
|
|
|
.. math::
|
|
\mathbf{C} &= \mathbf{A} \cdot b \\
|
|
\mathbf{c} &= \mathbf{A} \cdot \mathbf{b} \\
|
|
\mathbf{C} &= \mathbf{A} \cdot \mathbf{B}
|
|
|
|
.. note::
|
|
No size checking will be conducted, therefore no exceptions
|
|
for wrong usage (result will be nonsense).
|
|
|
|
:Example:
|
|
|
|
>>> m = matrix([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], \
|
|
[0, 0, 0, 1]]) * 5
|
|
>>> print(m)
|
|
[[5, 10, 15, 20], [25, 30, 35, 40], [45, 50, 55, 60], [0, 0, 0, 5]]
|
|
>>> m = matrix([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], \
|
|
[0, 0, 0, 1]]) * 5.
|
|
>>> print(m)
|
|
[[5.0, 10.0, 15.0, 20.0], [25.0, 30.0, 35.0, 40.0], [45.0, 50.0, 55.0, 60.0], [0.0, 0.0, 0.0, 5.0]]
|
|
>>> v = matrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) * \
|
|
vector([12, 12, 13])
|
|
>>> print(v)
|
|
[75, 186, 297]
|
|
>>> m = matrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) * \
|
|
matrix([[12, 12, 13], [14, 15, 16], [17, 18, 19]])
|
|
>>> print(m)
|
|
[[91, 96, 102], [220, 231, 246], [349, 366, 390]]
|
|
|
|
.. seealso::
|
|
:meth:`__rmul__`
|
|
"""
|
|
try: # matrix * matrix
|
|
return matrix([[sum(c*d for c, d in zip(a_row, b_col)) for
|
|
b_col in zip(*other)] for a_row in self])
|
|
except:
|
|
try: # matrix * vector
|
|
return vector([sum(c*d for c, d in zip(a_row, other)) for
|
|
a_row in self])
|
|
except: # matrix * scalar
|
|
return matrix([[a*other for a in a_row] for a_row in self])
|
|
|
|
def __rmul__(self, other):
|
|
r"""Scalar multiplication, dot product (inner product) or
|
|
vector-matrix multiplication. (new object)
|
|
|
|
:type other: scalar (or 1d list and 2d list)
|
|
|
|
.. math::
|
|
\mathbf{C} &= a \cdot \mathbf{B} \\
|
|
\mathbf{c} &= \mathbf{a} \cdot \mathbf{B} \\
|
|
\mathbf{C} &= \mathbf{A} \cdot \mathbf{B}
|
|
|
|
.. note::
|
|
No size checking will be conducted, therefore no exceptions
|
|
for wrong usage (result will be nonsense).
|
|
|
|
:Example:
|
|
|
|
>>> m = 5 * matrix([[1, 2, 3, 4], [5, 6, 7, 8], \
|
|
[9, 10, 11, 12], [0, 0, 0, 1]])
|
|
>>> print(m)
|
|
[[5, 10, 15, 20], [25, 30, 35, 40], [45, 50, 55, 60], [0, 0, 0, 5]]
|
|
>>> m = 5. * matrix([[1, 2, 3, 4], [5, 6, 7, 8], \
|
|
[9, 10, 11, 12], [0, 0, 0, 1]])
|
|
>>> print(m)
|
|
[[5.0, 10.0, 15.0, 20.0], [25.0, 30.0, 35.0, 40.0], [45.0, 50.0, 55.0, 60.0], [0.0, 0.0, 0.0, 5.0]]
|
|
|
|
.. seealso::
|
|
:meth:`__mul__` and :meth:`vector.__mul__` for vector * matrix
|
|
"""
|
|
try: # 2d list * matrix (matrix * matrix see matrix.__mul__)
|
|
return matrix([[sum(c*d for c, d in zip(a_row, b_col)) for
|
|
b_col in zip(*self)] for a_row in other])
|
|
except:
|
|
try: # 1d list * matrix (vector * matrix see vector.__mul__)
|
|
return vector([sum(c*d for c, d in zip(other, b_col)) for
|
|
b_col in zip(*self)])
|
|
except: # scalar * vector
|
|
return self*other
|
|
|
|
def __str__(self):
|
|
return str([*self])
|
|
|
|
def __repr__(self):
|
|
return "matrix(" + str(self) + ")"
|
|
|
|
@staticmethod
|
|
def zeros(m, n):
|
|
"""Returns a zero matrix of size mxn; m rows and n columns
|
|
filled with zeros.
|
|
|
|
:param m: number of rows of the matrix, e. g. 3
|
|
:type m: int
|
|
:param n: number of columns of the matrix, e. g. 3
|
|
:type n: int
|
|
|
|
:Example:
|
|
|
|
>>> m = matrix.zeros(3, 3)
|
|
>>> print(m)
|
|
[[0.0, 0.0, 0.0], [0.0, 0.0, 0.0], [0.0, 0.0, 0.0]]
|
|
"""
|
|
return matrix([[0. for i in range(n)] for j in range(m)])
|
|
|
|
@staticmethod
|
|
def rx(theta):
|
|
r"""Rotation matrix about the x direction.
|
|
|
|
Rotates the coordinate system of vectors
|
|
|
|
.. math::
|
|
\mathbf{R}_{x}(\theta) =
|
|
\begin{bmatrix}
|
|
1 & 0 & 0 & 0 \\
|
|
0 & \cos \theta & -\sin \theta & 0 \\
|
|
0 & \sin \theta & \cos \theta & 0 \\
|
|
0 & 0 & 0 & 1
|
|
\end{bmatrix}
|
|
"""
|
|
s = math.sin(theta)
|
|
c = math.cos(theta)
|
|
T = matrix([[ 1, 0, 0, 0],
|
|
[ 0, c,-s, 0],
|
|
[ 0, s, c, 0],
|
|
[ 0, 0, 0, 1]])
|
|
return T
|
|
|
|
@staticmethod
|
|
def ry(theta):
|
|
r"""Rotation matrix about the y direction.
|
|
|
|
Rotates the coordinate system of vectors
|
|
|
|
.. math::
|
|
\mathbf{R}_{y}(\theta) =
|
|
\begin{bmatrix}
|
|
\cos \theta & 0 & \sin \theta & 0 \\
|
|
0 & 1 & 0 & 0 \\
|
|
-\sin \theta & 0 & \cos \theta & 0 \\
|
|
0 & 0 & 0 & 1
|
|
\end{bmatrix}
|
|
"""
|
|
s = math.sin(theta)
|
|
c = math.cos(theta)
|
|
T = matrix([[ c, 0, s, 0],
|
|
[ 0, 1, 0, 0],
|
|
[-s, 0, c, 0],
|
|
[ 0, 0, 0, 1]])
|
|
return T
|
|
|
|
@staticmethod
|
|
def rz(theta):
|
|
r"""Rotation matrix about the z direction.
|
|
|
|
Rotates the coordinate system of vectors
|
|
|
|
.. math::
|
|
\mathbf{R}_{z}(\theta) =
|
|
\begin{bmatrix}
|
|
\cos \theta & -\sin \theta & 0 & 0 \\
|
|
\sin \theta & \cos \theta & 0 & 0 \\
|
|
0 & 0 & 1 & 0 \\
|
|
0 & 0 & 0 & 1
|
|
\end{bmatrix}
|
|
|
|
:Example:
|
|
|
|
.. math::
|
|
\mathbf{R}_{z}(\theta)
|
|
\begin{bmatrix}1 \\ 0 \\ 0 \\ 1\end{bmatrix}
|
|
&=
|
|
\begin{bmatrix}
|
|
\cos 90° & -\sin 90° & 0 & 0 \\
|
|
\sin 90° & \cos 90° & 0 & 0 \\
|
|
0 & 0 & 1 & 0 \\
|
|
0 & 0 & 0 & 1
|
|
\end{bmatrix}
|
|
\begin{bmatrix}1 \\ 0 \\ 0 \\ 1\end{bmatrix} \\
|
|
&=
|
|
\begin{bmatrix}
|
|
0 & -1 & 0 & 0 \\
|
|
1 & 0 & 0 & 0 \\
|
|
0 & 0 & 1 & 0 \\
|
|
0 & 0 & 0 & 1
|
|
\end{bmatrix}
|
|
\begin{bmatrix}1 \\ 0 \\ 0 \\ 1\end{bmatrix} =
|
|
\begin{bmatrix}0 \\ 1 \\ 0 \\ 1\end{bmatrix}
|
|
"""
|
|
s = math.sin(theta)
|
|
c = math.cos(theta)
|
|
T = matrix([[ c,-s, 0, 0],
|
|
[ s, c, 0, 0],
|
|
[ 0, 0, 1, 0],
|
|
[ 0, 0, 0, 1]])
|
|
return T
|
|
|
|
@staticmethod
|
|
def t(tx, ty, tz):
|
|
r"""Translation matrix
|
|
|
|
.. math::
|
|
\mathbf{T} =
|
|
\begin{bmatrix}
|
|
1 & 0 & 0 & t_x \\
|
|
0 & 1 & 0 & t_y \\
|
|
0 & 0 & 1 & t_z \\
|
|
0 & 0 & 0 & 1
|
|
\end{bmatrix}
|
|
"""
|
|
T = matrix([[ 1, 0, 0,tx],
|
|
[ 0, 1, 0,ty],
|
|
[ 0, 0, 1,tz],
|
|
[ 0, 0, 0, 1]])
|
|
return T
|
|
|
|
@staticmethod
|
|
def s(sx, sy=None, sz=None):
|
|
r"""Scaling matrix
|
|
|
|
uniform scaling if sx=sy=sz=s.
|
|
Note that scaling happens around the origin, so objects not
|
|
centered at the origin will have their centers move. To avoid
|
|
this, either scale the object when it's located at the origin,
|
|
or perform a translation afterwards to move the object back to
|
|
where it should be.
|
|
|
|
.. math::
|
|
\mathbf{S} =
|
|
\begin{bmatrix}
|
|
s_x & 0 & 0 & 0 \\
|
|
0 & s_y & 0 & 0 \\
|
|
0 & 0 & s_z & 0 \\
|
|
0 & 0 & 0 & 1
|
|
\end{bmatrix}
|
|
"""
|
|
# if not sy is not suitable because 0 is also false
|
|
if sy is None:
|
|
sy = sx
|
|
sz = sx
|
|
T = matrix([[sx, 0, 0, 0],
|
|
[ 0,sy, 0, 0],
|
|
[ 0, 0,sz, 0],
|
|
[ 0, 0, 0, 1]])
|
|
return T
|
|
|
|
@staticmethod
|
|
def transposed(a):
|
|
"""Transpose
|
|
"""
|
|
m = len(a)
|
|
n = len(a[0])
|
|
new = matrix.zeros(n, m)
|
|
for i in range(m):
|
|
for j in range(n):
|
|
new[j][i] = a[i][j]
|
|
return new
|
|
|
|
def transpose(self):
|
|
"""Transpose
|
|
"""
|
|
self[:] = matrix.transposed(self)
|
|
return self
|
|
|
|
def rotate_x(self, theta):
|
|
r"""Rotation about the x dirction.
|
|
|
|
.. math::
|
|
\begin{bmatrix}
|
|
xx & xy & xz & t_x \\
|
|
yx' & yy' & yz' & t_y' \\
|
|
zx' & zy' & zz' & t_z' \\
|
|
0 & 0 & 0 & h
|
|
\end{bmatrix} =
|
|
\begin{bmatrix}
|
|
1 & 0 & 0 & 0 \\
|
|
0 & \cos \theta & -\sin \theta & 0 \\
|
|
0 & \sin \theta & \cos \theta & 0 \\
|
|
0 & 0 & 0 & h
|
|
\end{bmatrix}
|
|
\begin{bmatrix}
|
|
xx & xy & xz & t_x \\
|
|
yx & yy & yz & t_y \\
|
|
zx & zy & zz & t_z \\
|
|
0 & 0 & 0 & h
|
|
\end{bmatrix}
|
|
"""
|
|
self[:] = matrix.rx(theta) * self
|
|
return self
|
|
|
|
def rotate_y(self, theta):
|
|
r"""Rotation about the y dirction.
|
|
|
|
.. math::
|
|
\begin{bmatrix}
|
|
xx' & xy' & xz' & t_x' \\
|
|
yx & yy & yz & t_y \\
|
|
zx' & zy' & zz' & t_z' \\
|
|
0 & 0 & 0 & h
|
|
\end{bmatrix} =
|
|
\begin{bmatrix}
|
|
\cos \theta & 0 & \sin \theta & 0 \\
|
|
0 & 1 & 0 & 0 \\
|
|
-\sin \theta & 0 & \cos \theta & 0 \\
|
|
0 & 0 & 0 & h
|
|
\end{bmatrix}
|
|
\begin{bmatrix}
|
|
xx & xy & xz & t_x \\
|
|
yx & yy & yz & t_y \\
|
|
zx & zy & zz & t_z \\
|
|
0 & 0 & 0 & h
|
|
\end{bmatrix}
|
|
"""
|
|
self[:] = matrix.ry(theta) * self
|
|
return self
|
|
|
|
def rotate_z(self, theta):
|
|
r"""Rotation about the z dirction.
|
|
|
|
.. math::
|
|
\begin{bmatrix}
|
|
xx' & xy' & xz' & t_x' \\
|
|
yx' & yy' & yz' & t_y' \\
|
|
zx & zy & zz & t_z \\
|
|
0 & 0 & 0 & h
|
|
\end{bmatrix} =
|
|
\begin{bmatrix}
|
|
\cos \theta & -\sin \theta & 0 & 0 \\
|
|
\sin \theta & \cos \theta & 0 & 0 \\
|
|
0 & 0 & 1 & 0 \\
|
|
0 & 0 & 0 & 1
|
|
\end{bmatrix}
|
|
\begin{bmatrix}
|
|
xx & xy & xz & t_x \\
|
|
yx & yy & yz & t_y \\
|
|
zx & zy & zz & t_z \\
|
|
0 & 0 & 0 & h
|
|
\end{bmatrix}
|
|
"""
|
|
self[:] = matrix.rz(theta) * self
|
|
return self
|
|
|
|
def translate(self, tx, ty, tz):
|
|
r"""Translation
|
|
|
|
.. math::
|
|
\begin{bmatrix}
|
|
xx & xy & xz & t_x' \\
|
|
yx & yy & yz & t_y' \\
|
|
zx & zy & zz & t_z' \\
|
|
0 & 0 & 0 & h
|
|
\end{bmatrix} =
|
|
\begin{bmatrix}
|
|
1 & 0 & 0 & t_x \\
|
|
0 & 1 & 0 & t_y \\
|
|
0 & 0 & 1 & t_z \\
|
|
0 & 0 & 0 & 1
|
|
\end{bmatrix}
|
|
\begin{bmatrix}
|
|
xx & xy & xz & t_x \\
|
|
yx & yy & yz & t_y \\
|
|
zx & zy & zz & t_z \\
|
|
0 & 0 & 0 & h
|
|
\end{bmatrix}
|
|
"""
|
|
self[:] = matrix.t(tx, ty, tz) * self
|
|
return self
|
|
|
|
def scale(self, sx, sy=None, sz=None):
|
|
r"""Scaling
|
|
|
|
uniform scaling if sx=sy=sz=s.
|
|
Note that scaling happens around the origin, so objects not
|
|
centered at the origin will have their centers move. To avoid
|
|
this, either scale the object when it's located at the origin,
|
|
or perform a translation afterwards to move the object back to
|
|
where it should be.
|
|
|
|
.. math::
|
|
\begin{bmatrix}
|
|
xx' & xy' & xz' & t_x' \\
|
|
yx' & yy' & yz' & t_y' \\
|
|
zx' & zy' & zz' & t_z' \\
|
|
0 & 0 & 0 & h
|
|
\end{bmatrix} =
|
|
\begin{bmatrix}
|
|
s_x & 0 & 0 & 0 \\
|
|
0 & s_y & 0 & 0 \\
|
|
0 & 0 & s_z & 0 \\
|
|
0 & 0 & 0 & 1
|
|
\end{bmatrix}
|
|
\begin{bmatrix}
|
|
xx & xy & xz & t_x \\
|
|
yx & yy & yz & t_y \\
|
|
zx & zy & zz & t_z \\
|
|
0 & 0 & 0 & h
|
|
\end{bmatrix}
|
|
"""
|
|
# if not sy is not suitable because 0 is also false
|
|
if sy is None:
|
|
sy = sx
|
|
sz = sx
|
|
self[:] = matrix.s(sx, sy, sz) * self
|
|
return self
|