Interpolate Before Mapping#

The add_mesh method has an interpolate_before_map argument that affects the way scalar data is visualized with colors. The effect of this can vary depending on the dataset’s topology and the chosen colormap.

This example serves to demo the difference and why we’ve chosen to enable this by default.

For more details, please see What is InterpolateScalarsBeforeMapping in VTK?

from __future__ import annotations

import pyvista as pv

Meshes are colored by the data on their nodes or cells - when coloring a mesh by data on its nodes, the values must be interpolated across the faces of cells. The process by which those scalars are interpolated is critical. If the interpolate_before_map is left off, the color mapping occurs at polygon points and colors are interpolated, which is generally less accurate whereas if the interpolate_before_map is on, then the scalars will be interpolated across the topology of the dataset which is more accurate.

To summarize, when interpolate_before_map is off, the colors are interpolated after rendering and when interpolate_before_map is on, the scalars are interpolated across the mesh and those values are mapped to colors.

So lets take a look at the difference:

# Load a cylinder which has cells with a wide spread
cyl = pv.Cylinder(direction=(0, 0, 1), height=2).elevation()

# Common display argument to make sure all else is constant
dargs = dict(scalars='Elevation', cmap='rainbow', show_edges=True)

p = pv.Plotter(shape=(1, 2))
p.add_mesh(
    cyl,
    interpolate_before_map=False,
    scalar_bar_args={'title': 'Elevation - not interpolated'},
    **dargs,
)
p.subplot(0, 1)
p.add_mesh(
    cyl,
    interpolate_before_map=True,
    scalar_bar_args={'title': 'Elevation - interpolated'},
    **dargs,
)
p.link_views()
p.camera_position = [(-1.67, -5.10, 2.06), (0.0, 0.0, 0.0), (0.00, 0.37, 0.93)]
p.show()
interpolate before map

Shown in the figure above, when not interpolating the scalars before mapping, the colors (RGB values, not scalars) are interpolated between the vertices by the underlying graphics library (OpenGL), and the colors shown are not accurate.

The same interpolation effect occurs for wireframe visualization too:

# Common display argument to make sure all else is constant
dargs = dict(scalars='Elevation', cmap='rainbow', show_edges=True, style='wireframe')

p = pv.Plotter(shape=(1, 2))
p.add_mesh(
    cyl,
    interpolate_before_map=False,
    scalar_bar_args={'title': 'Elevation - not interpolated'},
    **dargs,
)
p.subplot(0, 1)
p.add_mesh(
    cyl,
    interpolate_before_map=True,
    scalar_bar_args={'title': 'Elevation - interpolated'},
    **dargs,
)
p.link_views()
p.camera_position = [(-1.67, -5.10, 2.06), (0.0, 0.0, 0.0), (0.00, 0.37, 0.93)]
p.show()
interpolate before map

The cylinder mesh above is a great example dataset for this as it has a wide spread between the vertices (points are only at the top and bottom of the cylinder) which means high surface are of the mesh has to be interpolated.

However, most meshes don’t have such a wide spread and the effects of color interpolating are harder to notice. Let’s take a look at a wavelet example and try to figure out how the interpolate_before_map option affects its rendering.

wavelet = pv.Wavelet().clip('x')

# Common display argument to make sure all else is constant
dargs = dict(scalars='RTData', cmap='rainbow', show_edges=True)

p = pv.Plotter(shape=(1, 2))
p.add_mesh(
    wavelet,
    interpolate_before_map=False,
    scalar_bar_args={'title': 'RTData - not interpolated'},
    **dargs,
)
p.subplot(0, 1)
p.add_mesh(
    wavelet,
    interpolate_before_map=True,
    scalar_bar_args={'title': 'RTData - interpolated'},
    **dargs,
)
p.link_views()
p.camera_position = [(55.0, 16, 31), (-5.0, 0.0, 0.0), (-0.22, 0.97, -0.09)]
p.show()
interpolate before map

This time is pretty difficult to notice the differences - they are there, subtle, but present. The differences become more apparent when we decrease the number of colors in colormap. Let’s take a look at the differences when using eight discrete colors via the n_colors argument:

dargs = dict(scalars='RTData', cmap='rainbow', show_edges=True, n_colors=8)

p = pv.Plotter(shape=(1, 2))
p.add_mesh(
    wavelet,
    interpolate_before_map=False,
    scalar_bar_args={'title': 'RTData - not interpolated'},
    **dargs,
)
p.subplot(0, 1)
p.add_mesh(
    wavelet,
    interpolate_before_map=True,
    scalar_bar_args={'title': 'RTData - interpolated'},
    **dargs,
)
p.link_views()
p.camera_position = [(55.0, 16, 31), (-5.0, 0.0, 0.0), (-0.22, 0.97, -0.09)]
p.show()
interpolate before map

Left, interpolate_before_map OFF. Right, interpolate_before_map ON.

Now that is much more compelling. On the right, the contours of the scalar field are visible, but on the left, the contours are obscured due to the color interpolation by OpenGL. In both cases, the colors at the vertices are the same, the difference is how color is assigned between the vertices.

In our opinion, color interpolation is not a preferred default for scientific visualization and is why we have chosen to set the interpolate_before_map flag to True.

Total running time of the script: (0 minutes 1.943 seconds)

Gallery generated by Sphinx-Gallery