How to color triangle mesh of vtk files using python api?

How to import a VTK file and color triangle meshes using Python OVITO API. The official docs show no specific code about this. I’m very confused. Hope your kindly reply.

OVITO can import certain VTK files containing triangle meshes with per-face color information. In OVITO such a mesh will get imported as a TriangleMesh object. Here is a simple example of such a VTK file:

In the Python API of OVITO, the TriangleMesh class is not fully worked out yet – in contrast to the more comprehensive SurfaceMesh class. To assign colors either to the faces or the vertices of a TriangleMesh object, you have to use some undocumented functions. In OVITO 3.8.0, the class provides the following methods:

def TriangleMesh(DataObject):

    def get_vertices(self) -> NDArray[numpy.float64]: ...
    def get_faces(self) -> NDArray[numpy.int_]: ...
    def get_edge_visibility(self) -> NDArray[numpy.int_]: ...
    def get_vertex_colors(self) -> Optional[NDArray[numpy.float64]]: ...
    def get_vertex_pseudocolors(self) -> Optional[NDArray[numpy.float64]]: ...
    def get_face_colors(self) -> Optional[NDArray[numpy.float64]]: ...
    def get_face_pseudocolors(self) -> Optional[NDArray[numpy.float64]]: ...
    def get_normals(self) -> Optional[NDArray[numpy.float64]]: ...

    def set_vertices(self, coordinates: ArrayLike) -> None: ...
    def set_faces(self, vertex_indices: ArrayLike) -> None: ...
    def set_edge_visibility(self, edge_flags: ArrayLike) -> None: ...
    def set_vertex_colors(self, colors: Optional[ArrayLike]) -> None: ...
    def set_vertex_pseudocolors(self, pseudocolors: Optional[ArrayLike]) -> None: ...
    def set_face_colors(self, colors: Optional[ArrayLike]) -> None: ...
    def set_face_pseudocolors(self, pseudocolors: Optional[ArrayLike]) -> None: ...
    def set_normals(self, normals: Optional[ArrayLike]) -> None: ...

    def flip_faces(self) -> None: ...
    def determine_edge_visibility(self, threshold: float = 0.349) -> None: ...
    def remove_duplicate_vertices(self, eps: float) -> None: ...
    def clip_at_plane(self, normal: ovito.vis.Vector3, dist: float) -> None: ...

    def save_as_obj(self, path: os.PathLike) -> None: ...

You can call set_vertex_colors() or set_face_colors() with 4-component floating-point arrays to specify the colors of the mesh’s vertices or faces (but not both). 4 values per element in the range 0-1 specify the RGBA components for each vertex/face.

If you have a use case for this, we’ll make these functions public in a future version of OVITO and properly document them.

Hi, stukowski. I tried to use the method you provided but I’m not sure if I used it correctly. The code could run but the result was not what I expected.
This is the code snippet:

pipeline1 = import_file("converted_output.*.vtk")
pipeline1.add_to_scene()
data1 = pipeline1.compute()
n = data1.triangle_meshes['mesh_'].face_count
face_colors = np.zeros((n,4))
face_colors[:,0] = 1
face_colors[:,3] = 0.5
data1.triangle_meshes['mesh_'].set_face_colors(face_colors)

vp = Viewport(type = Viewport.Type.Top)
vp.render_anim(size=(1000,1000), filename="final.png", range=(0,pipeline1.source.num_frames-1),every_nth=1, renderer=OpenGLRenderer())

This is a rendered frame image:


The color should be red. However, it still keeps its origin color.I’m wondering what’s the problem. Hope for your reply.

The way you make changes to the mesh is wrong in this case. What you’ve done is change an independent copy of the mesh within the data1 snapshot, but that is not what Viewport.render_anim() will use. The viewport directly renders the output of the pipeline, which means you have to modify the mesh already within the pipeline. For instance, by adding a Python modifier function that performs the mesh coloring:

pipeline1 = import_file("converted_output.*.vtk")
pipeline1.add_to_scene()

def set_mesh_color(frame, data):
    n = data.triangle_meshes['mesh_'].face_count
    face_colors = np.zeros((n,4))
    face_colors[:,0] = 1
    face_colors[:,3] = 0.5
    data.triangle_meshes['mesh_'].set_face_colors(face_colors)
pipeline1.modifiers.append(set_mesh_color)

vp = Viewport(type = Viewport.Type.Top)
vp.render_anim(size=(1000,1000), filename="final.png", range=(0,pipeline1.source.num_frames-1),every_nth=1, renderer=OpenGLRenderer())

When rendering an animation, the viewport will evaluate the pipeline once for each trajectory frame. That means, the pipeline system will call your modifier function n times during the rendering process.

1 Like

Thanks very much. It works very well. Here I have another question : how to use OpenGLRenderer( ) and render_anim() method to render transparent background png images like that OVITO desktop does? Or to achieve this effect, it must be done with TachyonRenderer( ) ?

No, this has nothing to do with the renderer. Viewport.render_anim() currently does not support transparent backgrounds, because most video file formats such as AVI and MP4 do not support that either. But the render_image() function supports the alpha parameter. So, to generate a series of PNG image files with transparent backgrounds, you can simply call this function in a for-loop with varying frame parameter:

for frame in range(pipeline.source.num_frames):
    vp.render_image(filename=f"image{frame}.png", size=(640,480), alpha=True, frame=frame)

Thanks very much!