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]:

Rotation¶

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.        ]])

↑Top

←Home