Skip to content

matplotlib.collections.QuadMesh.set_array() input arg format is weird and undocumented #15388

Closed
@Tigraan

Description

@Tigraan

Bug report

Bug summary

Trying to give matplotlib.collections.QuadMesh.set_array() the same argument as was used by matplotlib.axes.Axes.pcolormesh() results in an error. Moreover, the argument format depends on the shading; an argument that is correct for shading='gouraud' will be accepted and produce spurious graphics for shading='flat'. In my opinion, the user should be able to use the same argument format for the update (via set_array or another method if that causes backwards compatibility issues) than used for creation (via pcolormesh), and not have to care about the shading when setting the data.

It was raised in a StackOverflow post from 2013 and the top answer (from 2015) gives workarounds, but I did not find anything in the issue tracker (here). It should at least be documented, the current doc only says "Set the image array from numpy array A".

Code for reproduction

import numpy as np
import matplotlib.pyplot as plt


def f(X, Y, t=0):
    return np.sin(X+t*np.pi)*np.sin(Y)

xmin, xmax = 0.0, 4*np.pi
ymin, ymax = 0.0, 5*np.pi
Nx, Ny = 50, 40
x, y = np.linspace(xmin, xmax, num=Nx), np.linspace(ymin, ymax, num=Ny)
X, Y = np.meshgrid(x, y, indexing='ij')
zstart = f(X, Y)

run_number = np.random.rand()

# Intialize figure stuff
fig = plt.figure()
axs = [
    plt.subplot(221, title='control flat'),
    plt.subplot(222, title='test flat'),
    plt.subplot(223, title='control gouraud'),
    plt.subplot(224, title='test gouraud'),
    ]

def drawcmesh(X, Y, z, ax, shading):
    ax.set_xlim(left=xmin, right=xmax)
    ax.set_ylim(bottom=ymin, top=ymax)
    cmesh = ax.pcolormesh(X, Y, z,
                          shading=shading, vmin=-1.5, vmax=1.5, cmap='hot')
    return cmesh



zstart = f(X, Y)
perm_mesh = [
    drawcmesh(X, Y, zstart, axs[1], shading='flat'),
    drawcmesh(X, Y, zstart, axs[3], shading='gouraud')
    ]
drawcmesh(X, Y, zstart, axs[0], shading='flat')
drawcmesh(X, Y, zstart, axs[2], shading='gouraud')

def update(t):
    z = f(X, Y, t)

    for i in range(4):
        ax = axs[i]
        if i<2:
            shading='flat'
        else:
            shading='gouraud'

        if i%2 == 0:
            # Control case: redraw by hand
            axs[i].clear()
            drawcmesh(X, Y, z, axs[i], shading=shading)
            axs[i].set_title('control {}'.format(shading))

        if i==1:
            # Flat shading

            # # The following would copy the syntax of plt.pcolormesh() but it
            # # results in an error
            # perm_mesh[0].set_array(z)

            # The following does not error out but the plot is later incorrect
            perm_mesh[0].set_array(z.ravel())

            # # The following works
            # perm_mesh[0].set_array(z[:-1, :-1].ravel())

        if i==3:
            # Gouraud shading

            # # The following would copy the syntax of plt.pcolormesh() but it
            # # results in an error
            # perm_mesh[1].set_array(z)

            # The following works
            perm_mesh[1].set_array(z.ravel())

            # # The following results in an error at draw time:

            
            # perm_mesh[1].set_array(z[:-1, :-1].ravel())

# # Uncomment this to check that the initial drawing for the test case is correct
# plt.show(block=True)

plt.show(block=False)
for t in np.linspace(0.0, 4.0, num=50):
    update(t)
    fig.canvas.draw()
    fig.canvas.flush_events()

Results
Cf commented code under # Flat shading and # Gouraud shading in the source for the various possibilities.

Spurious graphics for flat shading:

test-shadings

Error given by gouraud shading when dimensions do not match:

<current file>, line 93, in <module>
    fig.canvas.draw()
File "<anaconda-site-packages>\matplotlib\backends\backend_agg.py", line 388, in draw
    self.figure.draw(self.renderer)
File "<anaconda-site-packages>\matplotlib\artist.py", line 38, in draw_wrapper
    return draw(artist, renderer, *args, **kwargs)
File "<anaconda-site-packages>\matplotlib\figure.py", line 1709, in draw
    renderer, self, artists, self.suppressComposite)
File "<anaconda-site-packages>\matplotlib\image.py", line 135, in _draw_list_compositing_images
    a.draw(renderer)
File "<anaconda-site-packages>\matplotlib\artist.py", line 38, in draw_wrapper
    return draw(artist, renderer, *args, **kwargs)
File "<anaconda-site-packages>\matplotlib\axes\_base.py", line 2647, in draw
    mimage._draw_list_compositing_images(renderer, self, artists)
File "<anaconda-site-packages>\matplotlib\image.py", line 135, in _draw_list_compositing_images
    a.draw(renderer)
File "<anaconda-site-packages>\matplotlib\artist.py", line 38, in draw_wrapper
    return draw(artist, renderer, *args, **kwargs)
File "<anaconda-site-packages>\matplotlib\collections.py", line 2037, in draw
    self._meshWidth, self._meshHeight, coordinates)
File "<anaconda-site-packages>\matplotlib\collections.py", line 1985, in convert_mesh_to_triangles
    c = self.get_facecolor().reshape((meshHeight + 1, meshWidth + 1, 4))
ValueError: cannot reshape array of size 7644 into shape (50,40,4)

Matplotlib version

  • Operating system: Win10
  • Matplotlib version: 3.1.1 (from conda)
  • Matplotlib backend (print(matplotlib.get_backend())): Qt5Agg
  • Python version: 3.7.4

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions