pythonmatplotlibcolorbarmatplotlib-3d

Color on surface of a 3D plot does not match the data


I want to plot the intensity over a sphere using plot_surface. The problem is, that it is not possible for me to normalize the color and to set the colobar correctly. All solutions I found in the internet do not work for me.

The code I wrote uses the distance of the surface of the sphere to a specific vector d as a kind of intensity. Here is my code:

def FieldTranslation(N = 250, d = [-0.001, 0, 0], R = 1):
    d = np.array(d)
    t = np.linspace(0, np.pi, N)
    p = np.linspace(0, 2*np.pi, N)
    t, p = np.meshgrid(t, p)

    X = R * np.sin(t) * np.cos(p)
    Y = R * np.sin(t) * np.sin(p)
    Z = R * np.cos(t)

    C = np.sqrt((X - d[0])**2 + (Y - d[1])**2 + (Z - d[2])**2)

    CMax = np.maximum(C[..., 0], C[..., 1], C[..., 2])
    C = C / CMax

    if np.sum(d) == 0:
        print('round')
        C = np.round(C, 1)


    fig, ax = plt.subplots(subplot_kw={'projection': '3d'})

    scamap = plt.cm.ScalarMappable(cmap='inferno')
    fcolors = scamap.to_rgba(C)
    minn = -1 # fcolors.min()
    maxx = 1 # fcolors.max()
    s = ax.plot_surface(X, Y, Z, facecolors=fcolors, cmap='inferno', vmin = minn, vmax = maxx)
    fig.colorbar(s)
    plt.show()

This code creates an image like:

enter image description here

Here, the colobar does not fit to the colors of the plot and also, there should only be slide variations over the surface (when looking at the numbers in C).

If I set d = [0, 0, 0], the whole sphere turns black but the C values are all 1.

enter image description here

I already checked the forums and copied code from others, but nothing worked.


Solution

  • The colorbar drawn from s is drawn from the Z component.

    You should give fig.colorbar the custom scamap you created:

    X, Y, Z, C = field_translation()
    
    fig = plt.figure(figsize=(12, 6))
    ax_hist = fig.add_subplot(1, 2, 1)
    ax_surf = fig.add_subplot(1, 2, 2, projection="3d")
    
    ax_hist.hist(C.flat, bins=100)
    
    # vmin, vmax = 0.6, 1.0
    vmin, vmax = C.min(), C.max()
    
    norm = mpl.colors.Normalize(vmin, vmax)
    scamap = plt.cm.ScalarMappable(cmap="inferno", norm=norm)
    fcolors = scamap.to_rgba(C)
    
    ax_surf.plot_surface(X, Y, Z, facecolors=fcolors, cmap="inferno")
    fig.colorbar(scamap, ax=ax_surf)
    
    fig.tight_layout()
    plt.show()
    

    enter image description here