Revision 27cae4cd11fc38549fe85efa17c5bba33d2b340b (click the page title to view the current version)
Session. 3D Objects in Python
Learning Objectives
Key learning outcome Improve the understanding of the mathematical descriptions of 3D Motion, by testing implementations in Python.
Secondary outcomes if you have time, it is worth browsing different libraries and frameworks to build, visualise, and animate scenes using 3D objects.
Briefing
Recap: a simple object
Remember, last week we worked with this data structure in Python:
In [1]: print(vertices)
[[-1. 0.5 0.5]
[ 1. 0.5 0.5]
[ 0. -0.5 0.5]
[-1. 0.5 0.5]
[ 1. 0.5 0.5]
[ 0. 0.5 -0.5]
[-1. 0.5 0.5]
[ 0. -0.5 0.5]
[ 0. 0.5 -0.5]
[ 1. 0.5 0.5]
[ 0. -0.5 0.5]
[ 0. 0.5 -0.5]]
The rows are points in 3D. Note that there are only four distinct points. If we divide the matrix into sets of three rows, each triplet defines a triangle. These four triangles form the faces of an irregular tetrahedron.
This is a standard way to define a 3D object. More complex objects need more triangles.
Note that the textbook have vertices as column vectors, while we here use row vectors. This means that we need to transpose matrices when we translate textbook formulæ to python formulæ.
Consider, for instance a rotation matrix
\[ \begin{align} R = \begin{bmatrix} \cos(\pi/6) & -\sin(\pi/6) & 0 \\ \sin(\pi/6) & \cos(\pi/6) & 0 \\ 0 & 0 & 1 \end{bmatrix} \end{align} \]
This matrix is orthonormal, which we can check:
To rotate a vector \(\vec{v}\), we would calculate \(\vec{u}= R\cdot \vec{v}\), where \(\vec{u}\) and \(\vec{v}\) are column vectors. If you have a shape \(V\) where the columns are points, it could be rotated as \(U=R\cdot V\). In python this has to become
Translation is a little simpler, as we can do
Recall, to visualise,
ob1 = Poly3DCollection(vertices, linewidths=1, alpha=0.2)
ob2 = Poly3DCollection(newvertices, linewidths=1, alpha=0.2)
ob1.set_facecolor( [1, 0.5, 0.5] )
ob2.set_facecolor( [0.5, 1, 0.5] )
ob1.set_edgecolor([0,0,0])
ob2.set_edgecolor([0,0,0])
ax.add_collection3d(ob1)
ax.add_collection3d(ob2)
plt.show()
Homogeneous co-ordinates
As we know, a lot of operations are simpler in homogeneous co-ordinates.
We can add a one to the vertices as follows:
In [2]: vertices.shape
Out[2]: (12, 3)
In [3]: hvertices = hstack( [vertices, ones([12,1])] )
In [4]: print( hvertices)
[[-1. 0.5 0.5 1. ]
[ 1. 0.5 0.5 1. ]
[ 0. -0.5 0.5 1. ]
[-1. 0.5 0.5 1. ]
[ 1. 0.5 0.5 1. ]
[ 0. 0.5 -0.5 1. ]
[-1. 0.5 0.5 1. ]
[ 0. -0.5 0.5 1. ]
[ 0. 0.5 -0.5 1. ]
[ 1. 0.5 0.5 1. ]
[ 0. -0.5 0.5 1. ]
[ 0. 0.5 -0.5 1. ]]
In [5]:
Suppose vi have the rotational matrix \(R\) as above, and want to translate by a vector \(\vec{a} = [1,1,0]^T\). We can build the transform matrix as
Using this transform, we can transform the vertices in homogeneous co-ordinates, and also retrieve an inhomogeneous form.
STL files and the STL library
STL is a standard format for writing surface meshes. The following example defines the same tetrahedron as we have been working with, but the creates a mesh object using the numpy-stl library.
import numpy as np
from stl import mesh
corners = [ [-1,0.5,0.5], [+1,0.5,0.5], [0,-0.5,0.5], [0,0.5,-0.5] ]
vertices = np.array( corners )
face1 = [ 0, 1, 2 ]
face2 = [ 0, 1, 3 ]
face3 = [ 0, 2, 3 ]
face4 = [ 1, 2, 3 ]
faces = np.array( [ face1, face2, face3, face4 ] )
th = mesh.Mesh(np.zeros(faces.shape[0], dtype=mesh.Mesh.dtype))
for i, f in enumerate(faces):
for j in range(3):
print( (i,j), vertices[f[j],:])
th.vectors[i][j] = vertices[f[j]]
th.save('tetrahedron.stl')
Here, we save it to file. We can also show it.
from matplotlib import pyplot as plt
from mpl_toolkits.mplot3d.art3d import Poly3DCollection
ob = Poly3DCollection(th.vectors, linewidths=1, alpha=0.2)
ob.set_facecolor( [0.5, 0.5, 1] )
ob.set_edgecolor([0,0,0])
plt.ion()
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
plt.show()
ax.add_collection3d(ob)
This is essentially the same code as we have used before, just note how we access the vectors from the th
object. Note that the th.vectors
array is three-dimensional:
In [2]: cube.vectors
Out[2]:
array([[[-1. , 0.5, 0.5],
[ 1. , 0.5, 0.5],
[ 0. , -0.5, 0.5]],
[[-1. , 0.5, 0.5],
[ 1. , 0.5, 0.5],
[ 0. , 0.5, -0.5]],
[[-1. , 0.5, 0.5],
[ 0. , -0.5, 0.5],
[ 0. , 0.5, -0.5]],
[[ 1. , 0.5, 0.5],
[ 0. , -0.5, 0.5],
[ 0. , 0.5, -0.5]]], dtype=float32)
This makes it a lot more difficult to use low-level matrix operations to rotate and translate the object.
Exercise
Rotations and translations
Homogenous Coordinates
Motion defined by homogenous matrix
STL files and the STL library
- Load Model
- View Model
- Change Model
- Save Model