Skip to content
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

Surprising behavior of hatches in fill_between #11418

Open
mwaskom opened this issue Jun 11, 2018 · 19 comments
Open

Surprising behavior of hatches in fill_between #11418

mwaskom opened this issue Jun 11, 2018 · 19 comments
Labels
backend: pdf Difficulty: Hard https://matplotlib.org/devdocs/devel/contribute.html#good-first-issues Documentation topic: hatch

Comments

@mwaskom
Copy link

mwaskom commented Jun 11, 2018

Bug report

Hatches are invisible in the output of ax.fill_between when color is specified. They are present when color is absent or when facecolor is used.

I found #9894 which might be related, but seems to be a much narrower

Code for reproduction

import matplotlib.pyplot as plt

f, ax = plt.subplots()
ax.fill_between([0, 1], [0, 1], hatch="//")
f.savefig("test1.png")

f, ax = plt.subplots()
ax.fill_between([0, 1], [0, 1], hatch="//", color="g")
f.savefig("test2.png")

f, ax = plt.subplots()
ax.fill_between([0, 1], [0, 1], hatch="//", facecolor="g")
f.savefig("test3.png")

Actual outcome

test1.png:
test1

test2.png:
test2

test3.png:
test3

Expected outcome

I expected test2.png to have black hatch marks. I think what's happening is that the color= kwarg is being picked up globally by the artist and so the hatches in test2.png are there, they just have the same green color as the background. Unfortunately I don't think any hatch metadata other than the string code gets added to the artist, so I can't check.

Having color= globally change both the face and edge color of the artist makes a certain amount of sense, and I think the hatches are mostly inheriting their attributes from the kwargs that set the edge aesthetics. So I think I understand what's happening, but it took me a long time to figure out why the hatch argument had, apparently, no effect. I'll also note that the hatch demo is using the color= kwarg, but with ax.bar.

Matplotlib version

  • Operating system: MacOS
  • Matplotlib version: 2.2.2
  • Matplotlib backend (print(matplotlib.get_backend())): MacOSX
  • Python version: 3.6
  • Jupyter version (if applicable): NA
  • Other libraries: NA

This is a fresh install from conda to test, but I also saw it on matplotlib 2.2.0.

@mwaskom
Copy link
Author

mwaskom commented Jun 11, 2018

More surprising behavior: whether or not the hatches inherit the alpha= from the main artist varies across output formats. This is consistent across the three examples above but does reveal that the hatches are indeed present but invisible when specifying color:

f, ax = plt.subplots()
ax.fill_between([0, 1], [0, 1], hatch="//", color="g", alpha=.5)
f.savefig("test2.png")
f.savefig("test2.pdf")

test2.png:
test2

test2.pdf:
screenshot 2018-06-11 14 01 34

@jklymak
Copy link
Member

jklymak commented Jun 11, 2018

I think the work around is to call fill_between twice, once for the hatch and once for the facecolor.. I imagine an API proposal for how to make this controlable from the API would be welcome. Note the hatch.linewidth and hatch.color rcParams. You could probably also change those on the fly, though that is kludgey as well.

@mwaskom
Copy link
Author

mwaskom commented Jun 11, 2018

To be clear, I can accomplish what I want perfectly fine by using facecolor, but that wasn't obvious and using color just made it seem like hatch was broken somehow.

Short of adding a whole new set of kwargs that specifically pertain to hatches, I'm not exactly sure how to "solve" the first issue. But it's possible that a situation where the user specifies a hatch and color but not facecolor or edgecolor would be a reasonable place to fire a warning, as (I think?) we can be reasonably certain that the user doesn't mean to be doing that.

The second issue actually seems more like an actual bug?

@jklymak
Copy link
Member

jklymak commented Jun 11, 2018

Right, but sometimes folks want to color the hatch as well, vs using the default color. Calling twice is the workaround for that.

The second issue seems to definitely be a bug... Adding a pdf tag.

@jklymak jklymak added this to the v3.0 milestone Jun 11, 2018
@mwaskom
Copy link
Author

mwaskom commented Jun 11, 2018

Right, but sometimes folks want to color the hatch as well, vs using the default color. Calling twice is the workaround for that.

You can also accomplish that with edgecolor (but there's no way to get three distinct colors for the face, edges, and hatches; edgecolor overrides the hatch.color rcParam).

@jklymak
Copy link
Member

jklymak commented Jun 11, 2018

Ah, OK, I see. Probably rare that someone wants the edge and the hatch to be different, so thats probaby a decent API.

Tracking the docs through the color kwarg got me to: https://matplotlib.org/api/collections_api.html#matplotlib.collections.Collection.set_color which is pretty explicit that it'll set the edge and face colors. Where else in the docs should this be specified? FWIW, I'd personally just get rid of the color kwarg, but they outcry would be great!

@mwaskom
Copy link
Author

mwaskom commented Jun 11, 2018

Please do not get rid of the color kwarg! It is essential for higher-level APIs like seaborn.FacetGrid.

@mwaskom
Copy link
Author

mwaskom commented Jun 11, 2018

If you want to solve this with docs changes then I would suggest more clearly documenting how hatching works. AFAICT there's basically only the hatch demo, which demonstrates (without explication) a usage pattern that produces invisible hatches with fill_between, and then a subsection of the 2.0 release notes that documents how hatches get (some) customization by using the specified edge attributes.

@ImportanceOfBeingErnest
Copy link
Member

a situation where the user specifies a hatch and color but not facecolor or edgecolor would be a reasonable place to fire a warning, as (I think?) we can be reasonably certain that the user doesn't mean to be doing that.

This is the situation and it gives a perfectly expected result.

rect = plt.Rectangle((.2,.2),.6,.6, color="g", hatch="\\", fill=False)
plt.gca().add_patch(rect)

image


Probably rare that someone wants the edge and the hatch to be different, so thats probaby a decent API.

This is not rare at all. You would e.g. want a black border and differently colored hatches/faces in a bar plot. Currently it's only solvable by creating the same plot twice.


In general hatching is very underdevelopped. At one point @tacaswell mentionned that creating a MEP for it would be good; but I guess I forgot about it.

@mwaskom
Copy link
Author

mwaskom commented Jun 11, 2018

This is the situation and it gives a perfectly expected result.

Technically this does involve the user specifying the facecolor because setting fill=False changes its alpha channel:

rect = plt.Rectangle((0, 0), 1, 1, color="b", fill=False)
print(rect.get_facecolor())
print(rect.get_edgecolor())
(0.0, 0.0, 1.0, 0)
(0.0, 0.0, 1.0, 1)

It's also worth noting that it is possible to get a UserWarning in a similar context:

UserWarning: Setting the 'color' property will override the edgecolor or facecolor properties

@jklymak
Copy link
Member

jklymak commented Jun 11, 2018

In general hatching is very underdevelopped. At one point @tacaswell mentionned that creating a MEP for it would be good; but I guess I forgot about it.

We have a pretty big API that covers some rare use-cases. i.e. you could imagine a class for hatches where the hatch was an object that you manipulated. But we always have to balance if that extra API is really necessary given the occasional inconvenince of having to do things manually.

@tacaswell
Copy link
Member

For some fields hatching is not a rare use case (given how quickly it was reported when we broke some things about hatching in 2.0) in some fields.

You can also accomplish that with edgecolor (but there's no way to get three distinct colors for the face, edges, and hatches; edgecolor overrides the hatch.color rcParam).

Correct, the work to thread the hatch color through the API to be on the same footing as edge color and face color needs to be done. This includes both the set side to thread it through the plotting methods and the Artists and the backend side so that all of the backends that we ship respect the hatch color correctly.

As @ImportanceOfBeingErnest mentioned I commented else where this should get a MEP / some planning before the works get started and my rough estimate is that his would take a few weeks of full time effort by someone who knows the MPL code base to implement.

@ImportanceOfBeingErnest
Copy link
Member

I don't know about any big API. Hatching is essentially done on the artist level. There is currently no way to set the hatch linewidth, the hatch density (other than some divisor of some obscure default unit cell), the hatch alpha or the hatch angle.

So, yes, independent on the issue here, there is a need for a hatch API.


Coming back to the real issue here, I actually observe a difference in the facecolor, with or without hatching being used. Using matplotlib 2.2.2 and Qt4Agg as well as matplotlib master(from 2 days ago) and Qt5Agg.

f, (ax, ax2) = plt.subplots(ncols=2, figsize=(4,2))
ax.fill_between([0, 1], [0, 1], color="g", alpha=.5)
ax2.fill_between([0, 1], [0, 1], hatch="//", color="g", alpha=.5)
f.savefig("test2.png")
f.savefig("test2.pdf")
f.savefig("test2.svg")

png

test2

pdf

  • Foxit Reader:
    image

  • Acrobar Reader:
    image

svg

image

It seems in the pdf file output also depends on the viewer. In Foxit Reader, no alpha is shown.
In the svg file the hatching is missing but alpha is correctly applied to the face, not the hatch.

To make things even more weird, using facecolor instead of color in the above I get

png

test2

pdf

  • Foxit Reader:
    image

  • Acrobar Reader:
    image

svg

image

Here, the png shows a black hatching, while in the svg alpha seems to be applied to the hatch as well. In pdf it again depends on the viewer, Foxit shows black hatching, Acrobat shows hatching with alpha - however the result is still different compared to the color case.

In total there is not a single matching case here. I would say one needs to define what the expected outcome for each of the combinations of color, facecolor, edgecolor really should be.

@mwaskom
Copy link
Author

mwaskom commented Jun 12, 2018

Yikes!

@mwaskom
Copy link
Author

mwaskom commented Jun 12, 2018

So, if I can summarize:

  1. Part of this should be (narrowly but easily) addressed with some better documentation of how hatching interactions with other artist aesthetics.
  2. Part of this could be additionally addressed by adding a warning when a user is generating a collection with "invisible" hatches
  3. The ultimate solution is to develop a first-class API for controlling hatch aesthetics, but that will require some substantial design and implementation effort
  4. There are some problems with how different backends/writers (not 100% sure of the distinction) render hatches that should be fixed independent of what the user-facing API is.

Do you agree?

@WeatherGod
Copy link
Member

WeatherGod commented Jun 13, 2018 via email

@mwaskom
Copy link
Author

mwaskom commented Jun 13, 2018

I think 2 might be a bit of a red herring. We should make sure that the API does not lead one to do actions that don't make sense.

Absolutely. But in the meantime, the current approach is basically a hack that requires some knowledge of the implementation details; doing the most obvious thing fails in a confusing way. First-class support for hatching would be great, but also seems like a lot of work and I haven't seen anyone commit to it. So in the meantime, it might be helpful to give users some more guide rails. With that said I think that being conservative about issuing warnings is usually the right policy so I'm not going to push for it.

I think I can find time to make some small additions to the hatch demo and solve (1).

@mwaskom
Copy link
Author

mwaskom commented Jun 13, 2018

Here's another fun one: it doesn't seem possible to set hatch.linewidth on the fly using a with statement, but hatch.color works fine.

@jklymak jklymak modified the milestones: v3.0, v3.1 Jun 21, 2018
@jklymak jklymak added the Difficulty: Hard https://matplotlib.org/devdocs/devel/contribute.html#good-first-issues label Jun 21, 2018
@jklymak
Copy link
Member

jklymak commented Jun 21, 2018

I think I agree that it’d be nice if we had a big API for hatching/stippling. Not going to happen in next 1.5 weeks, and is a bit of an API argument about how to handle. However, I expect we need a FillStyle object similar to a Color object and allow folks to specify that instead of the string shortcuts (which would of course generate a set of default FillStyle objects).

Not sure if facecolor should be part of the FillStyle. naively, that seems the most complete way to do things.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backend: pdf Difficulty: Hard https://matplotlib.org/devdocs/devel/contribute.html#good-first-issues Documentation topic: hatch
Projects
None yet
Development

No branches or pull requests

6 participants