Skip to content

[Bug]: Updating the facecolors of a collection before its first draw has no effect #24479

Description

@anntzer

Bug summary

Create e.g. a pathcollection (a scatter plot) not mapping any array, but with directly set colors; then update one of the facecolors before the first draw of the collection. This change has no effect and gets cancelled.

Code for reproduction

import matplotlib.pyplot as plt

sc1 = plt.scatter([0, 1, 2], [0, 1, 2], c=["r", "g", "b"])
sc2 = plt.scatter([0, 1, 2], [2, 3, 4], c=["r", "g", "b"])
# Update sc1 before the first draw.
sc1.get_facecolors()[2] = (0, 0, 0, 1)  # i.e. black.
plt.gcf().canvas.draw()
# Update sc2 after the first draw.
sc2.get_facecolors()[2] = (0, 0, 0, 1)  # i.e. black

plt.show()

Actual outcome

test
Note that the third point of sc1 is incorrectly blue, whereas the third point of sc2 is correctly black.

Expected outcome

Both third points are black.

Additional information

First noted by @chahak13 in #24474 (comment).
I haven't done a full bisect but did check that this broke at some point between 3.3.4 and 3.4.0, and I suspect #18480 introduced the issue (in the changes related to update_scalarmappable); attn @efiring.

Operating system

fedora37

Matplotlib Version

3.7.0.dev538+g9b4985e185

Matplotlib Backend

qtagg

Python version

3.10

Jupyter version

ENOSUCHLIB

Installation

git checkout

Activity

  1. efiring commented on Nov 17, 2022

    @efiring
    Member

    This is not a bug. There was never any intention that one could change the colors by mutating the output of get_facecolors(). If you want to change the colors, you need to use the setter.

    import matplotlib.pyplot as plt
    
    sc1 = plt.scatter([0, 1, 2], [0, 1, 2], c=["r", "g", "b"])
    # Update sc1 before the first draw.
    fc = sc1.get_facecolors()
    fc[0] = (0, 0, 0, 1)  # i.e. black. instead of red
    sc1.set_facecolors(fc)
    plt.gcf().canvas.draw()
    
    plt.show()
  2. anntzer commented on Nov 17, 2022

    @anntzer
    ContributorAuthor

    Ah, thanks for pointing this out.
    Do you think get_facecolors() could return e.g. a read-only view on the colors to prevent the users from getting this wrong? Or at least the limitation should be documented. (Note that the limitation is somewhat subtle, as directly mutating the array will sometimes work (e.g. after a draw has been triggered, AFAICT).

  3. ksunden commented on Nov 17, 2022

    @ksunden
    Member

    Just for slightly more concrete discussion of the implementation, the primary thing the setter does (in regards to why just editing the getter's output is insufficient) is store the direct input to self._original_facecolor before actually computing the RGBA array

    This is done so that the colors can be recomputed according to what users expect.

    Recomputing happens in e.g. set_alpha, but also in update_scalarmappable, which is called directly from draw.

    By using the getter, editing and then using the setter, the values used to recompute colors are updated as well.

  4. Jacfger commented on Oct 31, 2023

    @Jacfger

    So while I understand the functions name implies the functions behavior but I believe it might be better to state the difference explicitly as what I had stated in #27233

    Would that be a good change? As mentioned in the issue I was attempting to change that but also there were a lot of get and set functions, so just to ask for some opinion

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions