Search code examples
pythonmatplotlibtriangulation

Shifting the triangles order to match colormap


This question is a sequel of a previous one but regarding this time the colormap and the order of the triangle. I want to interpolate experimental data over a surface so as to enable a continuous colormap with however the surface known only at its corner node. To interpolate, I put a canonical example which works quite well but fails on real data.

Indeed as shown in the example below, the initial triangulation results in two triangles with a huge gap between them, cf first picture. When the interpolation is done, it doesn't get any better and the colormap is also lost, cf. second picture. The best so far is by interverting z and y to get adjacent triangles from the beginning which results in a successful interpolation. However as you might notice in the third picture, the surface is tilted by 90° which is normal since I switch y for z and vice-versa.

However when I switch back y and z in the tri_surf function with ax.plot_trisurf(new.x, new_z, new.y, **kwargs), the colormap doesn't follow, cf. picture 4.

I thought of rotating the colormap in somehow or generate new triangles from the interpolated ones with triang = tri.Triangulation(new.x, new_z) but without any success. So any idea or hint about properly doing the initial triangulation with two adjacent triangles, as for the third picture, but with the surface oriented correclty and ultimately the colormap proportional to the Y-value.

import numpy
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
from matplotlib import cm
import matplotlib.tri as tri

x=numpy.array([0.00498316, 0.00498316, 0.00996632, 0.00996632])
y=numpy.array([-0.00037677, -0.00027191, -0.00078681, -0.00088475])
z=numpy.array([0., -0.0049926, 0., -0.00744763])

# Initial Triangle    
fig = plt.figure()
ax = Axes3D(fig)
triang = tri.Triangulation(x, y)

norm = plt.Normalize(vmax=y.max(), vmin=y.min())
ax.plot_trisurf(x, y, z, triangles=triang.triangles)

# Interpolated Triangle
fig = plt.figure()
ax = Axes3D(fig)
triang = tri.Triangulation(x, y)
refiner = tri.UniformTriRefiner(triang)
interpolator = tri.LinearTriInterpolator(triang, z)
new, new_z = refiner.refine_field(z, interpolator, subdiv=4)

kwargs = dict(triangles=new.triangles, cmap=cm.jet, norm=norm, linewidth=0,     antialiased=False)
ax.plot_trisurf(new.x, new.y, new_z, **kwargs)

# Best so far
fig = plt.figure()
ax = Axes3D(fig)
triang = tri.Triangulation(x, z)
refiner = tri.UniformTriRefiner(triang)
interpolator = tri.LinearTriInterpolator(triang, y)
new, new_z = refiner.refine_field(y, interpolator, subdiv=4)

kwargs = dict(triangles=new.triangles, cmap=cm.jet, norm=norm, linewidth=0, antialiased=False)
ax.plot_trisurf(new.x, new.y, new_z, **kwargs)

plt.show()

enter image description here enter image description here enter image description here enter image description here


Solution

  • Apparently the automatic triangulation doesn't produce the right triangles for you, but you can specify how you want your triangles manually:

    triang = tri.Triangulation(x, y, [[3,2,1],[1,2,0]])
    
    # alternatively:
    triang = tri.Triangulation(x, y, [[3,2,0],[1,3,0]])
    

    These two ways give rather different results:

    on the left: [[3,2,1],[1,2,0]], on the right: [[3,2,0],[1,3,0]]

    However, now the interpolation becomes awkward, because for some (x,y) there are multiple z-values.. One way of bypassing this issue is interpolating and plotting the 2 large triangles separately:

    import numpy
    from mpl_toolkits.mplot3d import Axes3D
    import matplotlib.pyplot as plt
    from matplotlib import cm
    import matplotlib.tri as tri
    
    
    def plot_refined_tri(x, y, z, ax, subdiv=4, **kwargs):
        triang = tri.Triangulation(x, y)
        refiner = tri.UniformTriRefiner(triang)
        interpolator = tri.LinearTriInterpolator(triang, z)
        new, new_z = refiner.refine_field(z, interpolator, subdiv=subdiv)
        ax.plot_trisurf(new.x, new.y, new_z, triangles=new.triangles, **kwargs)
    
    
    x=numpy.array([0.00498316, 0.00498316, 0.00996632, 0.00996632])
    y=numpy.array([-0.00037677, -0.00027191, -0.00078681, -0.00088475])
    z=numpy.array([0., -0.0049926, 0., -0.00744763])
    
    fig = plt.figure()
    ax = Axes3D(fig)
    # note: I normalized on z-values to "fix" the colormap
    norm = plt.Normalize(vmax=z.max(), vmin=z.min())
    kwargs = kwargs = dict(linewidth=0.2, cmap=cm.jet, norm=norm)
    
    idx = [3,2,1]
    plot_refined_tri(x[idx], y[idx], z[idx], ax, **kwargs)
    
    idx = [1,2,0]
    plot_refined_tri(x[idx], y[idx], z[idx], ax, **kwargs)
    
    plt.show()
    

    Result:

    This looks good