#!/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
[docs]def lcm(a, b):
"""Compute the lowest common multiple of a and b"""
return a/gcd(a, b)*b
[docs]class vector(list):
"""Use/create vector like lists
* size -> len(a)
* abs -> abs(a)
* dot -> a * b
* outer -> a @ b
use super constructor
use super __iter__
use super __setitem__
>>> v = vector([1,2,3,4,5])
>>> v[3:5] = [1,2]
>>> print(v)
[1, 2, 3, 1, 2]
>>> isinstance(v, vector)
True
use super __lt__(a, b)
use super __le__(a, b)
use super __eq__(a, b)
>>> v = vector([1,2,3,1,2])
>>> v2 = vector([1,2,3,1,2])
>>> v == v2
True
use super __ne__(a, b)
use super __ge__(a, b)
use super __gt__(a, b)
use super __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.__getslice__ 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)
"""
return vector([*self])
def __neg__(self):
"""- a (new object)
"""
return vector([-i for i in self])
def __add__(self, other):
"""a + b (new object)
: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)])
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)
: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.
a * b (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.
a * b (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__`
: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::
\vec{a} \otimes \vec{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 = \sqrt{\sum a_i^2}
:Example:
>>> norm([3, 4])
5
>>> norm(vector([3, 4]))
5
"""
return math.sqrt(self * self)
def __str__(self):
return str([*self])
def __repr__(self):
return "vector(" + str(self) + ")"
[docs] @staticmethod
def full(length, fill_value):
"""Returns a vector of length m or matrix of size m rows, n
columns 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)])
[docs] @staticmethod
def zeros(length):
"""Returns a zero vector of length m or matrix of size rows, n
columns filled with zeros.
:param length: length of the vector, e. g. 3
:type length: int
:Example:
>>> v = zeros(3)
>>> print(v)
[0.0, 0.0, 0.0]
"""
return vector.full(length, 0.)
[docs] @staticmethod
def ones(length):
"""Returns a vector of length m or matrix of size rows, n
columns filled with ones.
:param length: lhape of the vector, e. g. 3
:type length: int
:Example:
>>> v = ones(3)
>>> print(v)
[1.0, 1.0, 1.0]
"""
return vector.full(length, 1.)
[docs] @staticmethod
def random(shape, lmin=0.0, lmax=1.0):
"""Returns a random vector of length n or matrix of size m rows, n
columns filled with random numbers.
:Example:
>>> v = random(3)
>>> print(v)
[0.9172905912930438, 0.8908124278322492, 0.5256002790725927]
>>> v = 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(shape)])
[docs] @staticmethod
def normalize(a):
r"""Normalize a vector (i. e. the vector has a length of 1)
:type a: vector
.. math::
\vec{e}_a = \frac{\vec{a}}{|\vec{a}|}
.. seealso::
:meth:`norm` for a norm (magnitude) of a vector
"""
a_mag = abs(a)
return vector([i/a_mag for i in a])
[docs] @staticmethod
def ang(a, b):
return math.acos(a * b / (abs(a) * abs(b)))
[docs] @staticmethod
def conjugate(a):
"""
New vector object
:type a: list
"""
return vector([i.conjugate() for i in a])
[docs] @staticmethod
def re(a):
"""Return the real parts of a complex vector
:type a: list
"""
return vector([i.real for i in a])
[docs] @staticmethod
def im(a):
"""Return the imaginary parts of a complex vector
:type a: list
"""
return vector([i.imag for i in a])
[docs] @staticmethod
def abs(a):
"""Return modulus parts of a complex vector
:type a: list
"""
return vector([abs(i) for i in a])
[docs] @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])
[docs] @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::
\vec{c} = \vec{a} \times \vec{b}
"""
return [a[1]*b[2] - a[2]*b[1],
a[2]*b[0] - a[0]*b[2],
a[0]*b[1] - a[1]*b[0]]
[docs] def xyz(self):
return self[:3]
[docs] 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
[docs] 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
[docs] 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
[docs] 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
[docs] 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:
sy = sx
sz = sx
self[:] = matrix.s(sx, sy, sz) * self
return self
[docs] 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
[docs]class matrix(list):
"""Use/create matrix like list of lists
"""
def __getitem__(self, index):
"""
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[2])
[7, 8, 9, 0]
>>> 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]]
"""
# index: slice(stop), slice(start, stop[, step])
# for e. g. m[(1,3),:] -> index = ((1, 3), slice(None, None, None))
# use the list.__getslice__ method and convert result to vector
try: # 2d slicing (tuple of sclices)
item = [row.__getitem__(index[1]) for row in super().__getitem__(index[0])]
except: # 1d slicing
item = super().__getitem__(index)
return matrix(item) if isinstance(item, list) else item
[docs] @staticmethod
def rx(theta):
r"""Rotation matrix about the x direction.
Rotates the coordinate system of vectors
.. math::
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
[docs] @staticmethod
def ry(theta):
r"""Rotation matrix about the y direction.
Rotates the coordinate system of vectors
.. math::
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
[docs] @staticmethod
def rz(theta):
r"""Rotation matrix about the z direction.
Rotates the coordinate system of vectors
.. math::
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::
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
[docs] @staticmethod
def t(tx, ty, tz):
r"""Translation matrix
.. math::
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
[docs] @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::
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:
sy = sx
sz = sx
T = matrix([[sx, 0, 0, 0],
[ 0,sy, 0, 0],
[ 0, 0,sz, 0],
[ 0, 0, 0, 1]])
return T
def __mul__(self, other):
r"""Scalar multiplication, dot product (inner product) or
matrix-vector multiplication.
a * b (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.
a * b (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]]) * 5
>>> 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__`
: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) + ")"
[docs] 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
[docs] 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
[docs] 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
[docs] 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
[docs] 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:
sy = sx
sz = sx
self[:] = matrix.s(sx, sy, sz) * self
return self