Transform¶
Transfom represents one or more transform matrices, like translation matrices, scaling matrices, rotation matrices and projection matrices.
Identity¶
In [1]:
from py3d import Transform
Transform()
Out[1]:
Transform([[1., 0., 0., 0.], [0., 1., 0., 0.], [0., 0., 1., 0.], [0., 0., 0., 1.]])
In [3]:
Transform().tile(2)
Out[3]:
Transform([[[1., 0., 0., 0.], [0., 1., 0., 0.], [0., 0., 1., 0.], [0., 0., 0., 1.]], [[1., 0., 0., 0.], [0., 1., 0., 0.], [0., 0., 1., 0.], [0., 0., 0., 1.]]])
Translation¶
Translation Matrix¶
In py3d, transformations are represented as left-multiplication matrixes, point are represented as row vectors.
Left-multiplication translation matrix¶
In [5]:
from sympy import symbols, Matrix
x, y, z, dx, dy, dz = symbols("x y z dx dy dz")
point = Matrix([x, y, z, 1]).T
translation = Matrix([
[1, 0, 0, 0],
[0, 1, 0, 0],
[0, 0, 1, 0],
[dx, dy, dz, 1]
])
translation
Out[5]:
$\displaystyle \left[\begin{matrix}1 & 0 & 0 & 0\\0 & 1 & 0 & 0\\0 & 0 & 1 & 0\\dx & dy & dz & 1\end{matrix}\right]$
In [6]:
point * translation
Out[6]:
$\displaystyle \left[\begin{matrix}dx + x & dy + y & dz + z & 1\end{matrix}\right]$
In [7]:
dx_,dy_,dz_ = symbols("dx' dy' dz'")
translation_ = Matrix([
[1, 0, 0, 0],
[0, 1, 0, 0],
[0, 0, 1, 0],
[dx_, dy_, dz_, 1]
])
translation_ * translation
Out[7]:
$\displaystyle \left[\begin{matrix}1 & 0 & 0 & 0\\0 & 1 & 0 & 0\\0 & 0 & 1 & 0\\dx + dx' & dy + dy' & dz + dz' & 1\end{matrix}\right]$
In [8]:
translation * translation_
Out[8]:
$\displaystyle \left[\begin{matrix}1 & 0 & 0 & 0\\0 & 1 & 0 & 0\\0 & 0 & 1 & 0\\dx + dx' & dy + dy' & dz + dz' & 1\end{matrix}\right]$
Right-multiplication translation matrix¶
In [9]:
translation.T
Out[9]:
$\displaystyle \left[\begin{matrix}1 & 0 & 0 & dx\\0 & 1 & 0 & dy\\0 & 0 & 1 & dz\\0 & 0 & 0 & 1\end{matrix}\right]$
In [10]:
translation.T * point.T
Out[10]:
$\displaystyle \left[\begin{matrix}dx + x\\dy + y\\dz + z\\1\end{matrix}\right]$
In [11]:
import py3d
py3d.Transform.from_translation([2, 3, 4])
Out[11]:
Transform([[1., 0., 0., 0.], [0., 1., 0., 0.], [0., 0., 1., 0.], [2., 3., 4., 1.]])
In [13]:
import py3d
py3d.Transform.from_translation(x=1).tile(2)
Out[13]:
Transform([[[1., 0., 0., 0.], [0., 1., 0., 0.], [0., 0., 1., 0.], [1., 0., 0., 1.]], [[1., 0., 0., 0.], [0., 1., 0., 0.], [0., 0., 1., 0.], [1., 0., 0., 1.]]])
Translate a series of points
In [14]:
import py3d
points = py3d.Vector3.grid(range(5), range(5)).as_point()
points.color = py3d.Color(g=1)
py3d.render(points)
points @= py3d.Transform.from_translation(x=2, y=1, z=3)
points.color = py3d.Color(r=1)
py3d.render(points)
Out[14]:
In [15]:
import py3d
c=py3d.cube(0.5,0.2,0.3)
c @= py3d.Transform.from_translation(x=range(-10,10))
c.paint()
Out[15]:
Move a car
In [16]:
import py3d
car = py3d.car()
car.color = py3d.Color(r=1)
dt = 0.1
for i in range(10):
py3d.render(car, t=i*dt)
car.xyz @= py3d.Transform.from_translation(x=0.2)
py3d.show(viewpoint=[2,-10,2], lookat=[0,0,0], up=[0,0,1])
Scaling¶
In [17]:
import py3d
py3d.Transform.from_scaling(x=1, y=2, z=(1, 2, 3))
Out[17]:
Transform([[[1., 0., 0., 0.], [0., 2., 0., 0.], [0., 0., 1., 0.], [0., 0., 0., 1.]], [[1., 0., 0., 0.], [0., 2., 0., 0.], [0., 0., 2., 0.], [0., 0., 0., 1.]], [[1., 0., 0., 0.], [0., 2., 0., 0.], [0., 0., 3., 0.], [0., 0., 0., 1.]]])
In [18]:
import py3d
points = (py3d.rand(1000, 3)-0.5).U @ py3d.Transform.from_scaling(x=2)
points.as_point()
Out[18]:
Transposition¶
With property T, we can get transposed matrices of a given Transform. The matrices represented by the Transform will be transposed respectively and return a new Tranform with the same shape.
In [19]:
import py3d
transform=py3d.Transform([
[[ 0.54030231, 0.84147098, 0. , 0. ],
[-2.01953036, 1.29672553, 0. , 0. ],
[ 0. , 0. , 1. , 0. ],
[ 1.4 , 0. , 0. , 1. ]],
[[-0.41614684, 0.90929743, 0. , 0. ],
[-2.81882202, -1.29005519, 0. , 0. ],
[ 0. , 0. , 1. , 0. ],
[ 2.1 , 0. , 0. , 1. ]],
[[ 0.96017029, -0.2794155 , 0. , 0. ],
[ 2.17944089, 7.48932824, 0. , 0. ],
[ 0. , 0. , 1. , 0. ],
[ 6.8 , 0. , 0. , 1. ]]])
transform.T
Out[19]:
Transform([[[ 0.54030231, -2.01953036, 0. , 1.4 ], [ 0.84147098, 1.29672553, 0. , 0. ], [ 0. , 0. , 1. , 0. ], [ 0. , 0. , 0. , 1. ]], [[-0.41614684, -2.81882202, 0. , 2.1 ], [ 0.90929743, -1.29005519, 0. , 0. ], [ 0. , 0. , 1. , 0. ], [ 0. , 0. , 0. , 1. ]], [[ 0.96017029, 2.17944089, 0. , 6.8 ], [-0.2794155 , 7.48932824, 0. , 0. ], [ 0. , 0. , 1. , 0. ], [ 0. , 0. , 0. , 1. ]]])
Interpolation¶
In [20]:
import py3d, numpy
c = py3d.axis(1)
poses = py3d.Vector([
[0, 5, 1, 0, 2, 3],
[2, 7, 9, 1, 4, 8],
[10, 3, 8, 6, 9, 5]
], columns=["x", "y", "z", "rvx", "rvy", "rvz"])
t = py3d.Transform.from_rotation_vector(poses.a("rvx","rvy","rvz"))@py3d.Transform.from_translation(poses.a("x","y","z"))
c @ t.lerp(numpy.linspace(0, 4, 20), [0, 1, 4])
Out[20]:
Decomposition¶
Decompose a transformation matrix to scaling, rotation and translation.
In [21]:
t.scaling_vector
Out[21]:
Vector3([[1., 1., 1.], [1., 1., 1.], [1., 1., 1.]])
In [22]:
t.translation_vector
Out[22]:
Vector3([[ 0., 5., 1.], [ 2., 7., 9.], [10., 3., 8.]])
In [23]:
t.rotation_vector
Out[23]:
Vector3([[ 0. , -1.48528412, -2.22792618], [ 0.3018683 , 1.2074732 , 2.41494639], [-0.32727838, -0.49091756, -0.27273198]])
Projection¶
Camera and scene¶
In [24]:
import py3d
camera_axis=py3d.axis(2)
camera = py3d.camera(2000, 1000, 3000)
transform = py3d.Transform.from_translation([0,0,5]) @ py3d.Transform.from_rpy([py3d.pi/3,py3d.pi/6,0])
camera_axis @= transform
camera @= transform
py3d.render(camera, camera_axis)
Out[24]:
Project 3d points on xy plane¶
In [25]:
import py3d
p=(py3d.rand(1000, 3)-0.5).U+py3d.Vector3(x=1,y=1,z=2)
py3d.render(p.as_point())
p.z=0
py3d.render(p.as_point())
Out[25]:
Orthographic projection¶
In [26]:
import sympy
l, r, t, b, n, f = sympy.symbols("l r t b n f")
m_scale = sympy.Matrix([
[2/(r-l), 0, 0, 0],
[0, 2/(t-b), 0, 0],
[0, 0, 2/(n-f), 0],
[0, 0, 0, 1]
])
m_translate = sympy.Matrix([
[1, 0, 0, -(l+r)/2],
[0, 1, 0, -(b+t)/2],
[0, 0, 1, -(f+n)/2],
[0, 0, 0, 1]
])
o=sympy.simplify(m_scale*m_translate).transpose()
o
Out[26]:
$\displaystyle \left[\begin{matrix}\frac{2}{- l + r} & 0 & 0 & 0\\0 & \frac{2}{- b + t} & 0 & 0\\0 & 0 & - \frac{2}{f - n} & 0\\\frac{l + r}{l - r} & \frac{b + t}{b - t} & \frac{f + n}{f - n} & 1\end{matrix}\right]$
In [27]:
w, h = sympy.symbols("w h")
o=o.subs(l, -w/2).subs(r, w/2).subs(t, h/2).subs(b, -h/2)
o
Out[27]:
$\displaystyle \left[\begin{matrix}\frac{2}{w} & 0 & 0 & 0\\0 & \frac{2}{h} & 0 & 0\\0 & 0 & - \frac{2}{f - n} & 0\\0 & 0 & \frac{f + n}{f - n} & 1\end{matrix}\right]$
Use perspective fov and aspect to define a orthographic projection
In [28]:
fov, aspect, distance = sympy.symbols("fov a d")
o=o.subs(w, aspect * h).subs(h, distance*sympy.tan(fov/2)*2)
o
Out[28]:
$\displaystyle \left[\begin{matrix}\frac{1}{a d \tan{\left(\frac{fov}{2} \right)}} & 0 & 0 & 0\\0 & \frac{1}{d \tan{\left(\frac{fov}{2} \right)}} & 0 & 0\\0 & 0 & - \frac{2}{f - n} & 0\\0 & 0 & \frac{f + n}{f - n} & 1\end{matrix}\right]$
In [29]:
x, y, z = sympy.symbols("x y z")
sympy.Matrix([x, y, z, 1]).T @ o
Out[29]:
$\displaystyle \left[\begin{matrix}\frac{x}{a d \tan{\left(\frac{fov}{2} \right)}} & \frac{y}{d \tan{\left(\frac{fov}{2} \right)}} & - \frac{2 z}{f - n} + \frac{f + n}{f - n} & 1\end{matrix}\right]$
Perspective projection¶
In [30]:
from py3d import Transform, pi, Vector3
projection = Transform.from_perspective(pi/2, 1.2, 0, 1000)
projection
Out[30]:
array([[ 0.83333333, 0. , 0. , 0. ], [ 0. , 1. , 0. , 0. ], [ 0. , 0. , -1. , 1. ], [ 0. , 0. , -0. , 0. ]])