Files
pylib/pylib/mathematics.py
Daniel Weschke 033cb92dc4 add tui module and example, add contex manager cd and the method run_file in helper
import vendor modules only once during import

change text color for the method cad_wireframe in geometry_plot_pylab and add hide key
2020-01-13 10:25:33 +01:00

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