Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Make QuadMesh arguments with defaults keyword_only #20237

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
May 25, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions doc/api/next_api_changes/deprecations/20237-TH.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
QuadMesh signature
~~~~~~~~~~~~~~~~~~
The ``QuadMesh`` signature ::

def __init__(meshWidth, meshHeight, coordinates,
antialiased=True, shading=False, **kwargs)

is deprecated and replaced by the new signature ::

def __init__(coordinates, *, antialiased=True, shading=False, **kwargs)

In particular:

- *coordinates* must now be a (M, N, 2) array-like. Previously, the grid
shape was separately specified as (*meshHeight* + 1, *meshWidth* + 1) and
*coordinates* could be an array-like of any shape with M * N * 2 elements.
- all parameters except *coordinates* are keyword-only now.
18 changes: 8 additions & 10 deletions lib/matplotlib/axes/_axes.py
Original file line number Diff line number Diff line change
Expand Up @@ -6164,16 +6164,12 @@ def pcolormesh(self, *args, alpha=None, norm=None, cmap=None, vmin=None,

X, Y, C, shading = self._pcolorargs('pcolormesh', *args,
shading=shading, kwargs=kwargs)
Ny, Nx = X.shape
X = X.ravel()
Y = Y.ravel()

# convert to one dimensional arrays
coords = np.stack([X, Y], axis=-1)
# convert to one dimensional array
C = C.ravel()
coords = np.column_stack((X, Y)).astype(float, copy=False)
collection = mcoll.QuadMesh(Nx - 1, Ny - 1, coords,
antialiased=antialiased, shading=shading,
**kwargs)

collection = mcoll.QuadMesh(
coords, antialiased=antialiased, shading=shading, **kwargs)
snap = kwargs.get('snap', rcParams['pcolormesh.snap'])
collection.set_snap(snap)
collection.set_alpha(alpha)
Expand All @@ -6184,6 +6180,8 @@ def pcolormesh(self, *args, alpha=None, norm=None, cmap=None, vmin=None,

self.grid(False)

coords = coords.reshape(-1, 2) # flatten the grid structure; keep x, y

# Transform from native to data coordinates?
t = collection._transform
if (not isinstance(t, mtransforms.Transform) and
Expand Down Expand Up @@ -6360,7 +6358,7 @@ def pcolorfast(self, *args, alpha=None, norm=None, cmap=None, vmin=None,
else:
raise ValueError("C must be 2D or 3D")
collection = mcoll.QuadMesh(
nc, nr, coords, **qm_kwargs,
coords, **qm_kwargs,
alpha=alpha, cmap=cmap, norm=norm,
antialiased=False, edgecolors="none")
self.add_collection(collection, autolim=False)
Expand Down
86 changes: 75 additions & 11 deletions lib/matplotlib/collections.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,17 @@
line segments).
"""

import inspect
import math
from numbers import Number
import warnings

import numpy as np

import matplotlib as mpl
from . import (_api, _path, artist, cbook, cm, colors as mcolors, docstring,
hatch as mhatch, lines as mlines, path as mpath, transforms)
from ._enums import JoinStyle, CapStyle
import warnings


# "color" is excluded; it is a compound setter, and its docstring differs
Expand Down Expand Up @@ -1966,6 +1968,35 @@ class QuadMesh(Collection):
"""
Class for the efficient drawing of a quadrilateral mesh.

A quadrilateral mesh is a grid of M by N adjacent qudrilaterals that are
defined via a (M+1, N+1) grid of vertices. The quadrilateral (m, n) is
defind by the vertices ::

(m+1, n) ----------- (m+1, n+1)
/ /
/ /
/ /
(m, n) -------- (m, n+1)

The mesh need not be regular and the polygons need not be convex.

Parameters
----------
coordinates : (M+1, N+1, 2) array-like
The vertices. ``coordinates[m, n]`` specifies the (x, y) coordinates
of vertex (m, n).

antialiased : bool, default: True

shading : {'flat', 'gouraud'}, default: 'flat'

Notes
-----
There exists a deprecated API version ``QuadMesh(M, N, coords)``, where
the dimensions are given explicitly and ``coords`` is a (M*N, 2)
array-like. This has been deprecated in Matplotlib 3.5. The following
describes the semantics of this deprecated API.

A quadrilateral mesh consists of a grid of vertices.
The dimensions of this array are (*meshWidth* + 1, *meshHeight* + 1).
Each vertex in the mesh has a different set of "mesh coordinates"
Expand All @@ -1989,22 +2020,55 @@ class QuadMesh(Collection):
vertex at mesh coordinates (0, 0), then the one at (0, 1), then at (0, 2)
.. (0, meshWidth), (1, 0), (1, 1), and so on.

*shading* may be 'flat', or 'gouraud'
"""
def __init__(self, meshWidth, meshHeight, coordinates,
antialiased=True, shading='flat', **kwargs):
def __init__(self, *args, **kwargs):
# signature deprecation since="3.5": Change to new signature after the
# deprecation has expired. Also remove setting __init__.__signature__,
# and remove the Notes from the docstring.
#
# We use lambdas to parse *args, **kwargs through the respective old
# and new signatures.
try:
# Old signature:
# The following raises a TypeError iif the args don't match.
w, h, coords, antialiased, shading, kwargs = (
lambda meshWidth, meshHeight, coordinates, antialiased=True,
shading=False, **kwargs:
(meshWidth, meshHeight, coordinates, antialiased, shading,
kwargs))(*args, **kwargs)
except TypeError as exc:
# New signature:
# If the following raises a TypeError (i.e. args don't match),
# just let it propagate.
coords, antialiased, shading, kwargs = (
lambda coordinates, antialiased=True, shading=False, **kwargs:
(coordinates, antialiased, shading, kwargs))(*args, **kwargs)
coords = np.asarray(coords, np.float64)
else: # The old signature matched.
_api.warn_deprecated(
"3.5",
message="This usage of Quadmesh is deprecated: Parameters "
"meshWidth and meshHeights will be removed; "
"coordinates must be 2D; all parameters except "
"coordinates will be keyword-only.")
coords = np.asarray(coords, np.float64).reshape((h + 1, w + 1, 2))
# end of signature deprecation code

super().__init__(**kwargs)
self._meshWidth = meshWidth
self._meshHeight = meshHeight
# By converting to floats now, we can avoid that on every draw.
self._coordinates = np.asarray(coordinates, float).reshape(
(meshHeight + 1, meshWidth + 1, 2))
_api.check_shape((None, None, 2), coordinates=coords)
self._coordinates = coords
self._meshWidth = self._coordinates.shape[1] - 1
self._meshHeight = self._coordinates.shape[0] - 1
self._antialiased = antialiased
self._shading = shading

self._bbox = transforms.Bbox.unit()
self._bbox.update_from_data_xy(coordinates.reshape(
((meshWidth + 1) * (meshHeight + 1), 2)))
self._bbox.update_from_data_xy(self._coordinates.reshape(-1, 2))

# Only needed during signature deprecation
__init__.__signature__ = inspect.signature(
lambda self, coordinates, *,
antialiased=True, shading='flat', **kwargs: None)

def get_paths(self):
if self._paths is None:
Expand Down
72 changes: 72 additions & 0 deletions lib/matplotlib/tests/test_collections.py
Original file line number Diff line number Diff line change
Expand Up @@ -714,6 +714,78 @@ def test_singleton_autolim():
np.testing.assert_allclose(ax.get_xlim(), [-0.06, 0.06])


@pytest.mark.parametrize('flat_ref, kwargs', [
(True, {}),
(False, {}),
(True, dict(antialiased=False)),
(False, dict(transform='__initialization_delayed__')),
])
@check_figures_equal(extensions=['png'])
def test_quadmesh_deprecated_signature(
fig_test, fig_ref, flat_ref, kwargs):
# test that the new and old quadmesh signature produce the same results
# remove when the old QuadMesh.__init__ signature expires (v3.5+2)
from matplotlib.collections import QuadMesh

x = [0, 1, 2, 3.]
y = [1, 2, 3.]
X, Y = np.meshgrid(x, y)
X += 0.2 * Y
coords = np.stack([X, Y], axis=-1)
assert coords.shape == (3, 4, 2)
C = np.linspace(0, 2, 12).reshape(3, 4)

ax = fig_test.add_subplot()
ax.set(xlim=(0, 5), ylim=(0, 4))
if 'transform' in kwargs:
kwargs['transform'] = mtransforms.Affine2D().scale(1.2) + ax.transData
qmesh = QuadMesh(coords, **kwargs)
qmesh.set_array(C)
ax.add_collection(qmesh)

ax = fig_ref.add_subplot()
ax.set(xlim=(0, 5), ylim=(0, 4))
if 'transform' in kwargs:
kwargs['transform'] = mtransforms.Affine2D().scale(1.2) + ax.transData
with pytest.warns(MatplotlibDeprecationWarning):
qmesh = QuadMesh(4 - 1, 3 - 1,
coords.copy().reshape(-1, 2) if flat_ref else coords,
**kwargs)
qmesh.set_array(C.flatten() if flat_ref else C)
ax.add_collection(qmesh)


@check_figures_equal(extensions=['png'])
def test_quadmesh_deprecated_positional(fig_test, fig_ref):
# test that positional parameters are still accepted with the old signature
# and work correctly
# remove when the old QuadMesh.__init__ signature expires (v3.5+2)
from matplotlib.collections import QuadMesh

x = [0, 1, 2, 3.]
y = [1, 2, 3.]
X, Y = np.meshgrid(x, y)
X += 0.2 * Y
coords = np.stack([X, Y], axis=-1)
assert coords.shape == (3, 4, 2)
coords_flat = coords.copy().reshape(-1, 2)
C = np.linspace(0, 2, 12).reshape(3, 4)

ax = fig_test.add_subplot()
ax.set(xlim=(0, 5), ylim=(0, 4))
qmesh = QuadMesh(coords, antialiased=False, shading='gouraud')
qmesh.set_array(C)
ax.add_collection(qmesh)

ax = fig_ref.add_subplot()
ax.set(xlim=(0, 5), ylim=(0, 4))
with pytest.warns(MatplotlibDeprecationWarning):
qmesh = QuadMesh(4 - 1, 3 - 1, coords.copy().reshape(-1, 2),
False, 'gouraud')
qmesh.set_array(C)
ax.add_collection(qmesh)


def test_quadmesh_set_array():
x = np.arange(4)
y = np.arange(4)
Expand Down