Skip to content

ENH: Extend gouraud shading to support data at quadrilateral centers#31362

Open
lucaznch wants to merge 7 commits intomatplotlib:mainfrom
lucaznch:centered-gouraud
Open

ENH: Extend gouraud shading to support data at quadrilateral centers#31362
lucaznch wants to merge 7 commits intomatplotlib:mainfrom
lucaznch:centered-gouraud

Conversation

@lucaznch
Copy link
Copy Markdown

@lucaznch lucaznch commented Mar 24, 2026

PR summary

This PR extends the gouraud shading in Axes.pcolormesh to support data defined at the centers of quadrilaterals, where X and Y are one larger than Z in each dimension. In this situation, gouraud automatically converts the grid to match the data's shape by replacing each quadrilateral with a point at its center, computed as the average of its four corners, and then applies Gouraud shading.

  • Why is this change necessary / what problem does it solve?

    pcolormesh with shading='gouraud' requires the data to be defined at the corners of quadrilaterals, meaning X, Y and Z must have the same shape. If the user is working with data defined at the centers, so that X and Y are one larger in each dimension, and tries to use shading='gouraud', a TypeError is raised. Extending gouraud solves this by allowing such grids without requiring users to manually reshape them.

  • What is the reasoning for this implementation?

    Following discussion on the issue and in this PR, the approach taken is to extend the existing gouraud shading. This keeps the API simpler while allowing gouraud to handle both grid/data configurations.

Example:

import matplotlib.pyplot as plt
import numpy as np

nrows, ncols = 3, 5
Z = np.arange(nrows * ncols).reshape(nrows, ncols)
x = np.arange(ncols + 1)
y = np.arange(nrows + 1)

fig, axs = plt.subplots(2, 2, layout='constrained')

# Data at corners, requires X and Y the same shape as Z.
axs[0, 0].pcolormesh(x[:-1], y[:-1], Z, shading='nearest')
axs[0, 1].pcolormesh(x[:-1], y[:-1], Z, shading='gouraud')

# Data at centers, requires X and Y one larger than Z.
axs[1, 0].pcolormesh(x, y, Z, shading='flat')
axs[1, 1].pcolormesh(x, y, Z, shading='gouraud')

Closes #8422

AI Disclosure

I used AI tools to help proofread and improve the grammar of this PR description. The code logic and implementation are my own.

PR checklist

@github-actions github-actions bot added topic: rcparams Documentation: examples files in galleries/examples labels Mar 24, 2026
@github-actions
Copy link
Copy Markdown

Thank you for opening your first PR into Matplotlib!

If you have not heard from us in a week or so, please leave a new comment below and that should bring it to our attention. Most of our reviewers are volunteers and sometimes things fall through the cracks. We also ask that you please finish addressing any review comments on this PR and wait for it to be merged (or closed) before opening a new one, as it can be a valuable learning experience to go through the review process.

You can also join us on gitter for real-time discussion.

For details on testing, writing docs, and our review process, please see the developer guide.
Please let us know if (and how) you use AI, it will help us give you better feedback on your PR.

We strive to be a welcoming and open project. Please follow our Code of Conduct.

@lucaznch
Copy link
Copy Markdown
Author

I realized I forgot to run boilerplate.py to update pyplot.py, which is why some tests were failing. Ive just amended the commit and pushed the updated branch!

@melissawm melissawm moved this to Needs review in First Time Contributors Mar 25, 2026
@scottshambaugh
Copy link
Copy Markdown
Contributor

New docs:
image

Copy link
Copy Markdown
Contributor

@scottshambaugh scottshambaugh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I kind of prefer extending 'gouraud' rather than making a separate 'centered-gouraud', but can see @timhoffm rationale for keeping them separate.

Code and docs all look good to me!

@melissawm melissawm moved this from Needs review to Waiting for author in First Time Contributors Mar 25, 2026
@lucaznch
Copy link
Copy Markdown
Author

Thank you for the review and suggestion. I updated that part to use f-strings.

Copy link
Copy Markdown
Member

@timhoffm timhoffm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My mental model is that we need to primarily distinguish the data semantics: Are the values at the corners of the quadrilaterals (n_grid = n_data + 1) or at the centers (n_grid = ndata)?

Therefore switching should only work between "nearest" <-> "gouraud" and "flat" <-> "centered-gouraud"

discrete continuous
data at corners "nearest" "gouraud"
data at centers "flat" "centered-gouraud"

"auto" just selects between "flat" and "nearest" based on the geometry.

We probably should rewrite the whole shading logic more in these terms. While I don't require this to be part of this PR, the descriptions added here should be consistent with that logic.

Edit: I had mixed up "nearest" and "flat" above. Now corrected.

@jklymak
Copy link
Copy Markdown
Member

jklymak commented Mar 26, 2026

If you want to be completely parallel you could have "flat-gouraud" and "nearest-gouraud" and then have "gouraud" be like "auto" and just do the right thing.

@timhoffm
Copy link
Copy Markdown
Member

If you want to be completely parallel you could have "flat-gouraud" and "nearest-gouraud" and then have "gouraud" be like "auto" and just do the right thing.

Hm, while this would be structurally the same, the naming would be a bit awkward. "flat" stems from the idea that there is a constant value across the quadrilateral. "nearest" meas "take the color from the nearest grid node. Both do not rhyme with a continous shading that gouraud implies. I believe if we want more consistent naming we would need to rethink the the whole set of names "constant" and "gouraud" would be the automatic ones (and discourage "auto" in favor of "constant"). Not sure about the grid-specific ones; maybe "nodes-constant", "centered-condstant", "nodes-gouraud", "centered-gouraud".
There is also a slight semantic difference in that the gouraud variants define the colors at exactly the points and interpolate in between, whereas the constants can be interpreted either as directly meaning the whole area "the quadrilateral has as constant value" or as defining the color on a single point ("color at the quadrilateral center") and filling with constant extrapolation.

It's a bit the question whether we want to advertize/encourage automatic response to the shape constallations, or whether we prefer explicit specification that the data points are meant to be on the grid nodes or the quadrilateral centers.

@jklymak
Copy link
Copy Markdown
Member

jklymak commented Mar 26, 2026

Fair enough, that is awkward.

Just to review the context of the original changes: the old behaviour of flat if x and y matched the shape of Z was intolerable: it dropped a row and column of Z and shifted the data over by half a cell in x and y. Yet I'd bet the amount of data that is stored that way outnumbers data where x and y are the borders of the cell by 100:1. So matplotlib (and Matlab still) were actively dropping data and changing its spatial phase without even warning the user.

Here the problem is less, and the opposite. Occasional users who have x and y having one ore element than Z in each dimension cannot make a plot. I think we should just help those users by automatically plotting the data at the centres, and maybe emiting a warning. But I'm fine if we want to have a different named option to allow this.

Implement a variant of Gouraud shading that supports
grids with dimensions one greater than the data in each dimension.
Add tests and documentation.
Update type hints and rcParams validation.
Include a What's New entry.

Fixes matplotlib#8422
@lucaznch
Copy link
Copy Markdown
Author

Thanks for the feedback. Ive made the suggested changes and pushed everything. I noticed however that several automated tests are failing. I’m not sure if these are related to my changes or unrelated.

@melissawm melissawm moved this from Waiting for author to Needs review in First Time Contributors Mar 27, 2026
@timhoffm
Copy link
Copy Markdown
Member

We still need to make a decision. On whether automatic behavior based on the grid vs data shape is desired and should be the default (in which case gouraud should cover both).

I've originally been in favor of dedicated names for all the use cases to have the analogy with 'nearest' and 'flat' and force an explicit decision on how you want to render your data. But can see the appeal of an automatic setting so that "it just works".

@rcomer
Copy link
Copy Markdown
Member

rcomer commented Mar 30, 2026

I suspect the majority of users do not bother passing “flat” or “nearest” to the shading parameter but just leave it to “auto”. Could we deprecate those options for shading and introduce a new parameter that distinguishes whether x,y represent “centers” or “corners” for those who really want to nail it down? So we end up with something like

  • shading = “box”, “gouraud” (default “box”)
  • locations = “centers”, “corners”, “auto” (default “auto”)

@lucaznch
Copy link
Copy Markdown
Author

lucaznch commented Mar 30, 2026

From the discussion so far, it seems there are two main approaches being considered:

1. Extend gouraud with automatic handling

  • keep the current grid semantics
  • gouraud expects X, Y and Z to have the same shape
  • add support for the gouraud case where X and Y are one dimension larger than Z, by automatically converting them.
    • possibly emit a warning

2. Introduce centered-gouraud

  • introduce data semantics

  • gouraud expects data at the corners of quadrilaterals, requiring X, Y and Z to have the same shape

  • centered-gouraud expects data at the centers of quadrilaterals, and therefore X and Y to be one dimension larger than Z

  • pcolormesh now provides both discrete and continuous shading for each data semantics:

    discrete continuous
    data at corners "nearest" "gouraud"
    data at centers "flat" "centered-gouraud"

    Switching is defined within the same data semantics:

    • nearest $\leftrightarrow$ gouraud
    • flat $\leftrightarrow$ centered-gouraud

Another approach suggested by @rcomer:

3. Separate rendering style from data semantics

  • introduce data semantics explicitly

    • add a parameter like location = 'centers' | 'corners' | 'auto'
  • change shading to describe the rendering style

    • e.g. shading = 'discrete' | 'continuous'
  • for example:

    Z = np.arange(nrows * ncols).reshape(nrows, ncols)
    x = np.arange(ncols)
    y = np.arange(nrows)
    
    _, ax = plt.subplots()
    
    # ax.pcolormesh(x, y, Z, shading='nearest') would be equivalent to:
    ax.pcolormesh(x, y, Z, shading='discrete', location='corners')
    
    # ax.pcolormesh(x, y, Z, shading='gouraud') would be equivalent to:
    ax.pcolormesh(x, y, Z, shading='continuous', location='corners')
    
    x = np.arange(ncols + 1)
    y = np.arange(nrows + 1)
    
    # ax.pcolormesh(x, y, Z, shading='flat') would be equivalent to:
    ax.pcolormesh(x, y, Z, shading='discrete', location='centers')
    
    # ax.pcolormesh(x, y, Z, shading='centered-gouraud') would be equivalent to:
    ax.pcolormesh(x, y, Z, shading='continuous', location='centers')

My current implementation follows approach 2.

I’m happy to adapt the PR depending on the preferred direction.

@ayshih
Copy link
Copy Markdown
Contributor

ayshih commented Mar 30, 2026

I like @rcomer's suggestion of using two keyword parameters to decouple the shading type from the location type. That said, I'm not a fan of calling the option 'box' (and I'm even less of a fan of 'discrete'/'continuous'). How about 'step' (a la the histtype option) to go alongside 'gouraud'?

@timhoffm
Copy link
Copy Markdown
Member

timhoffm commented Mar 30, 2026

If locations="auto" is the default would anybody really bother to set this to a more specific value? I doubt that. The only reason could be that you want to make sure that the data is plotted as desired, which actually means it's has a specific structure. But that could be checked on the data shapes up-front. I suspect such a parameter wound not be of practical use and we could always add that later.

Then, I'm rather tempted to aim at shading : {'constant', 'gouraud'}, default: 'constant' with additional backward compatibility of keeping 'nearest', 'flat' and making 'constant' a synonym for the current 'auto'.

In theory, we could alternatively promote 'nearest' or 'flat' to the general one if you feel that naming would be much better. It's only slightly weirder than promoting 'grouraud'.

@jklymak
Copy link
Copy Markdown
Member

jklymak commented Mar 30, 2026

I agree 'flat' and 'nearest' are bad terms for Gouraud.

Given that, I'd vote for 1) above - just fix the data for the user, and emit a warning if it has been fixed. I think this case will be very rare, and a user who wants to suppress the warning can supply the properly shaped data. That way the toggle is auto/gouraud. I can't imagine a case where someone would be mad that they didn't get an error here.

@lucaznch
Copy link
Copy Markdown
Author

lucaznch commented Mar 31, 2026

About your shading: {'constant', 'gouraud'} idea @timhoffm, just to clarify:

  • Does this mean that constant would support both cases where X, Y have the same shape as Z, and where they are one dimension larger?
  • And similarly for gouraud, would it be extended to handle both shapes?

Regarding this PR, I’m thinking of proceeding with approach 1, extending gouraud to handle the (n+1, m+1) case with a warning.

The shading simplification idea seems like a great improvement. I’d be happy to either include it in this PR if that is preferred, or work on it in another PR.

@timhoffm
Copy link
Copy Markdown
Member

timhoffm commented Apr 1, 2026

Yes, 'constant' and 'gouraud' would handle both cases and automatically decide what to do.

Regarding this PR, I’m thinking of proceeding with approach 1, extending gouraud to handle the (n+1, m+1) case with a warning.

Yes, but without the warning. If we have an automatism, that's intended behavior, so a warning is not justified (in the same way that "auto" does not warn).

Let's focus this PR on the implementation. We can have a follow-up to discuss whether and how to evolve the API.

@lucaznch
Copy link
Copy Markdown
Author

lucaznch commented Apr 1, 2026

Understood, thanks for the clarification. In the meantime I noticed two minor things in the docstrings that could maybe be improved.

@jklymak
Copy link
Copy Markdown
Member

jklymak commented Apr 1, 2026

I'd still suggest a warning as we are dropping data (the coords of the cell edges in favour of the midpoints). "Auto" doesn't drop anything.

@lucaznch
Copy link
Copy Markdown
Author

lucaznch commented Apr 1, 2026

I suppose the warning depends on how this automatic handling is intended to be presented, whether it is a fallback for incompatible grids one larger than C, or an extension of gouraud to support that same case (and therefore needing to update user documentation (specifically this example).

Given that it was mentioned that this case is not very common, i think it makes sense to go with a warning.

@lucaznch
Copy link
Copy Markdown
Author

lucaznch commented Apr 1, 2026

Updated the implementation to handle grids one larger than C in gouraud shading.

@timhoffm
Copy link
Copy Markdown
Member

timhoffm commented Apr 1, 2026

I would have a problem with if the only way to use centered Gouraud shading always produces a warning. A regular feature must always be usable without a warning.

In addition, this mustn’t be a „fallback for incompatible grids“. An automatism is only justified if every outcome is expected for the respective inputs, i.e. we must be able to assume that the user knows whether their data is to be on the grid nodes or centers. If we cannot, doing something with a warning is the wrong approach. Instead we then cannot do the automatism, but would have to resort to two targeted shading variants that the user would have to specify explicitly.

@lucaznch
Copy link
Copy Markdown
Author

lucaznch commented Apr 1, 2026

Understood, thanks.

- Extend gouraud shading to support data defined at the centers of quadrilaterals.
- Add tests.
- Add and update documentation.
- Add a What's New entry.
@github-actions github-actions bot added the Documentation: examples files in galleries/examples label Apr 5, 2026
@lucaznch
Copy link
Copy Markdown
Author

lucaznch commented Apr 5, 2026

Updated the implementation following the discussion: extended gouraud to support data defined at the centers without a warning.

Added and updated docs (including the two fixes in the pcolormesh API docs I pointed out above), added tests, and included a What's New entry.

@lucaznch
Copy link
Copy Markdown
Author

lucaznch commented Apr 5, 2026

New docs:
Screenshot from 2026-04-06 00-12-09


# Data at centers, requires X and Y the same shape as Z.
ax.pcolormesh(x[:-1], y[:-1], Z, shading='nearest')
ax.pcolormesh(x[:-1], y[:-1], Z, shading='gouraud')
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
ax.pcolormesh(x[:-1], y[:-1], Z, shading='gouraud')
ax.pcolormesh(x[:-1], y[:-1], Z, shading='centered-gouraud')

Also this should probably use a ..plot: directive so that the release note actually shows the plot with the differences and runs this code

@melissawm melissawm moved this from Needs review to Waiting for author in First Time Contributors Apr 10, 2026
@lucaznch lucaznch changed the title ENH: Add centered-gouraud shading to pcolormesh ENH: Extend gouraud shading to support data at quadrilateral centers Apr 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

Status: Waiting for author

Development

Successfully merging this pull request may close these issues.

plt.pcolormesh shape mismatch when shading='gouraud'

8 participants