Unexpected fallback to Qt5Agg and exception with some zooming conditions
afvincent opened this issue ยท 6 comments
Disclaimer
I am neither sure if there is actually 1 or 2 issues, nor that it is really an issue with mplcairo or something more subtle but definitively part of the vanilla Matplotlib backends ๐ .
Code snippet
# -*- coding: utf-8 -*-
"""
Filename: example_issue_mplcairo.py
"""
import numpy as np
import matplotlib
matplotlib.use("module://mplcairo.qt") # my default, but just to be sure
#matplotlib.use("Qt5Agg") # no error is triggered with this backend
print("#0 " + matplotlib.get_backend())
import matplotlib.pyplot as plt
# Prepare data
# ############
# NB: using "real" data bc I couldn't trigger the issue with artificial ones.
data = np.array([
[0.9010783810870, 0.01630277666922, 0.9094683896745, 0.01026045742829],
[0.9570786503693, 0.16706294351760, 0.9450353328361, 0.05505800869503],
[0.9571383849633, 0.16753128736241, 0.9528604472226, 0.08115013837996],
[0.9571854383087, 0.16790131216395, 0.9560082084967, 0.09529819473078],
[0.9572052469505, 0.16805737890754, 0.9571888177871, 0.10130912344386],
[0.9572556034709, 0.16845490694844, 0.9595591931432, 0.11474075575411],
[0.9572566034023, 0.16846281207996, 0.9636070458793, 0.14280470855909],
[0.9572595678960, 0.16848625101237, 0.9636078558762, 0.14281109000281],
[0.9572636787008, 0.16851875978305, 0.9636083939103, 0.14281532901640],
])
raw_x0 = data[:, 0]
raw_y0 = data[:, 1]
raw_x1 = data[:, 2]
raw_y1 = data[:, 3]
vmin = max(raw_x0.min(), raw_x1.min())
vmax = min(raw_x0.max(), raw_x1.max())
xx = np.concatenate((raw_x0[(raw_x0 >= vmin) & (raw_x0 <= vmax)],
raw_x1[(raw_x1 >= vmin) & (raw_x1 <= vmax)]))
xx.sort()
y0 = np.interp(xx, raw_x0, raw_y0)
y1 = np.interp(xx, raw_x1, raw_y1)
# Prepare figure
# ##############
# Weirdly, the return value from `matplotlib.get_backend()` changes after
# instantiating the figure.
print("#1 " + matplotlib.get_backend())
fig, ax = plt.subplots(num="example_anntzer", clear=True)
print("#2 " + matplotlib.get_backend())
# Plot
# ####
ax.plot(raw_x0, raw_y0, marker="v", color="tab:red", mfc="white")
ax.plot(raw_x1, raw_y1, marker="^", color="tab:blue", mfc="white")
ax.fill_between(xx, y0, y1, interpolate=True, color="tab:purple", alpha=0.5)
fig.tight_layout()
print("#3 " + matplotlib.get_backend())
fig.show()
# Zooming on the x-values corresponding to the last group of values for x0
# will trigger an error with 'mplcairo', but not with 'Qt5Agg'.
Actual outcome
First, why would matplotlib.get_backend()
stop returning 'module://mplcairo.qt'
? See
In [1]: run example_issue_mplcairo.py
#0 module://mplcairo.qt
#1 module://mplcairo.qt
#2 Qt5Agg
#3 Qt5Agg
Besides, when zooming like on the following screenshot
I get an error with the following traceback
In [2]: Traceback (most recent call last):
File "/home/adrien/anaconda3/lib/python3.6/site-packages/matplotlib/backends/backend_qt5.py", line 519, in _draw_idle
self.draw()
File "/home/adrien/anaconda3/lib/python3.6/site-packages/mplcairo/base.py", line 225, in draw
self.figure.draw(self.get_renderer())
File "/home/adrien/anaconda3/lib/python3.6/site-packages/matplotlib/artist.py", line 55, in draw_wrapper
return draw(artist, renderer, *args, **kwargs)
File "/home/adrien/anaconda3/lib/python3.6/site-packages/matplotlib/figure.py", line 1475, in draw
renderer, self, artists, self.suppressComposite)
File "/home/adrien/anaconda3/lib/python3.6/site-packages/matplotlib/image.py", line 141, in _draw_list_compositing_images
a.draw(renderer)
File "/home/adrien/anaconda3/lib/python3.6/site-packages/matplotlib/artist.py", line 55, in draw_wrapper
return draw(artist, renderer, *args, **kwargs)
File "/home/adrien/anaconda3/lib/python3.6/site-packages/matplotlib/axes/_base.py", line 2607, in draw
mimage._draw_list_compositing_images(renderer, self, artists)
File "/home/adrien/anaconda3/lib/python3.6/site-packages/matplotlib/image.py", line 141, in _draw_list_compositing_images
a.draw(renderer)
File "/home/adrien/anaconda3/lib/python3.6/site-packages/matplotlib/artist.py", line 55, in draw_wrapper
return draw(artist, renderer, *args, **kwargs)
File "/home/adrien/anaconda3/lib/python3.6/site-packages/matplotlib/collections.py", line 911, in draw
Collection.draw(self, renderer)
File "/home/adrien/anaconda3/lib/python3.6/site-packages/matplotlib/artist.py", line 55, in draw_wrapper
return draw(artist, renderer, *args, **kwargs)
File "/home/adrien/anaconda3/lib/python3.6/site-packages/matplotlib/collections.py", line 337, in draw
self._offset_position)
ValueError: invalid value (typically too big) for the size of the input (surface, pattern, etc.)
Expected outcome
In [1]: run example_issue_mplcairo.py
#0 module://mplcairo.qt
#1 module://mplcairo.qt
#2 module://mplcairo.qt
#3 module://mplcairo.qt
and no exception raised when zooming on the plot :/.
Platform
- Python: 3.6 from conda
- Matplotlib: 2.2.2 from conda
- mplcairo: from a nighty build made by @anntzer when I was struggling with installing mplcairo...
What's mplcairo.__version__
? (That'll give me the commit the nightly is from.)
I see where the exception is coming from. Do you, by any chance, have the path.simplify_threshold set to a nonzero value? Does setting it to zero fix the issue?
If it does, what's happening is the following:
In order to improve the performance of drawing scatterplots with various sizes and colors, whenever it is asked to draw a PathCollection (the class behind scatterplots), mplcairo keeps a cache of the scatter elements drawn at various sizes (lazily populated on-demand), so that rasterization is performed only once and later uses can just "stamp" the image from the buffer.
Unfortunately, fill_between is also implemented using PathCollection, so mplcairo tries to do the same here... and ends up trying to draw the fill_between polygon at an enourmous magnification (at that point it doesn't realize yet that the thing will be cropped).
I plan to fix that by following the same strategy as for draw_markers, which is to fall back on "naive" drawing when the polygon is bigger than the canvas size (for example).
I cannot reproduce the backend changing issue (I get module://mplcairo.qt all the time). What's your version of ipython, do you have anything of interest in your ipython_config.py, do you use e.g. %matplotlib auto
, do you use MPLBACKEND, etc.?
FWIW (I still have to read #4).
The mplcairo version that I am using:
In [1]: mplcairo.__version__
Out[1]: '0.1a1.post15+g322e722'
Excerpt from my matplotlibrc file:
### SAVING FIGURES
#path.simplify : True # When True, simplify paths by removing "invisible"
# points to reduce file size and increase rendering
# speed
#path.simplify_threshold : 0.1 # The threshold of similarity below which
# vertices will be removed in the simplification
# process
#path.snap : True # When True, rectilinear axis-aligned paths will be snapped to
# the nearest pixel when certain criteria are met. When False,
# paths will never be snapped.
#path.sketch : None # May be none, or a 3-tuple of the form (scale, length,
# randomness).
# *scale* is the amplitude of the wiggle
# perpendicular to the line (in pixels). *length*
# is the length of the wiggle along the line (in
# pixels). *randomness* is the factor by which
# the length is randomly scaled.
Excerpt from my .bashrc file:
export MPLBACKEND="module://mplcairo.qt"
I am using IPython 6.2.1 (from conda). AFAICT, I am not using a peculiar ipython_config.py
file (there is no such file in $HOME/.ipython/
), but indeed it looks like an IPython issue:
python example_issue_mplcairo.py
#0 module://mplcairo.qt
#1 module://mplcairo.qt
#2 module://mplcairo.qt
#3 module://mplcairo.qt
OK, I can reproduce it now. What's your ~/.config/matplotlib/matplotlibrc?