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

Skip to content

Commit 3285cac

Browse files
authored
Merge pull request #21174 from meeseeksmachine/auto-backport-of-pr-19343-on-v3.5.x
Backport PR #19343 on branch v3.5.x (Enh improve agg chunks error)
2 parents 48f9d0a + 1a6ecfe commit 3285cac

File tree

4 files changed

+139
-13
lines changed

4 files changed

+139
-13
lines changed

lib/matplotlib/backends/backend_agg.py

Lines changed: 52 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -148,18 +148,65 @@ def draw_path(self, gc, path, transform, rgbFace=None):
148148
c = c[ii0:ii1]
149149
c[0] = Path.MOVETO # move to end of last chunk
150150
p = Path(v, c)
151+
p.simplify_threshold = path.simplify_threshold
151152
try:
152153
self._renderer.draw_path(gc, p, transform, rgbFace)
153154
except OverflowError as err:
154-
raise OverflowError(
155-
"Exceeded cell block limit (set 'agg.path.chunksize' "
156-
"rcparam)") from err
155+
msg = (
156+
"Exceeded cell block limit in Agg.\n\n"
157+
"Please reduce the value of "
158+
f"rcParams['agg.path.chunksize'] (currently {nmax}) "
159+
"or increase the path simplification threshold"
160+
"(rcParams['path.simplify_threshold'] = "
161+
f"{mpl.rcParams['path.simplify_threshold']:.2f} by "
162+
"default and path.simplify_threshold = "
163+
f"{path.simplify_threshold:.2f} on the input)."
164+
)
165+
raise OverflowError(msg) from None
157166
else:
158167
try:
159168
self._renderer.draw_path(gc, path, transform, rgbFace)
160169
except OverflowError as err:
161-
raise OverflowError("Exceeded cell block limit (set "
162-
"'agg.path.chunksize' rcparam)") from err
170+
cant_chunk = ''
171+
if rgbFace is not None:
172+
cant_chunk += "- can not split filled path\n"
173+
if gc.get_hatch() is not None:
174+
cant_chunk += "- can not split hatched path\n"
175+
if not path.should_simplify:
176+
cant_chunk += "- path.should_simplify is False\n"
177+
if len(cant_chunk):
178+
msg = (
179+
"Exceeded cell block limit in Agg, however for the "
180+
"following reasons:\n\n"
181+
f"{cant_chunk}\n"
182+
"we can not automatically split up this path to draw."
183+
"\n\nPlease manually simplify your path."
184+
)
185+
186+
else:
187+
inc_threshold = (
188+
"or increase the path simplification threshold"
189+
"(rcParams['path.simplify_threshold'] = "
190+
f"{mpl.rcParams['path.simplify_threshold']} "
191+
"by default and path.simplify_threshold "
192+
f"= {path.simplify_threshold} "
193+
"on the input)."
194+
)
195+
if nmax > 100:
196+
msg = (
197+
"Exceeded cell block limit in Agg. Please reduce "
198+
"the value of rcParams['agg.path.chunksize'] "
199+
f"(currently {nmax}) {inc_threshold}"
200+
)
201+
else:
202+
msg = (
203+
"Exceeded cell block limit in Agg. Please set "
204+
"the value of rcParams['agg.path.chunksize'], "
205+
f"(currently {nmax}) to be greater than 100 "
206+
+ inc_threshold
207+
)
208+
209+
raise OverflowError(msg) from None
163210

164211
def draw_mathtext(self, gc, x, y, s, prop, angle):
165212
"""Draw mathtext using :mod:`matplotlib.mathtext`."""

lib/matplotlib/tests/test_agg.py

Lines changed: 84 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,12 @@
88

99
from matplotlib import (
1010
collections, path, pyplot as plt, transforms as mtransforms, rcParams)
11-
from matplotlib.image import imread
11+
from matplotlib.backends.backend_agg import RendererAgg
1212
from matplotlib.figure import Figure
13+
from matplotlib.image import imread
14+
from matplotlib.path import Path
1315
from matplotlib.testing.decorators import image_comparison
16+
from matplotlib.transforms import IdentityTransform
1417

1518

1619
def test_repeated_save_with_alpha():
@@ -72,10 +75,10 @@ def test_marker_with_nan():
7275

7376
def test_long_path():
7477
buff = io.BytesIO()
75-
76-
fig, ax = plt.subplots()
77-
np.random.seed(0)
78-
points = np.random.rand(70000)
78+
fig = Figure()
79+
ax = fig.subplots()
80+
points = np.ones(100_000)
81+
points[::2] *= -1
7982
ax.plot(points)
8083
fig.savefig(buff, format='png')
8184

@@ -251,3 +254,79 @@ def test_draw_path_collection_error_handling():
251254
ax.scatter([1], [1]).set_paths(path.Path([(0, 1), (2, 3)]))
252255
with pytest.raises(TypeError):
253256
fig.canvas.draw()
257+
258+
259+
@pytest.fixture
260+
def chunk_limit_setup():
261+
N = 100_000
262+
dpi = 500
263+
w = 5*dpi
264+
h = 6*dpi
265+
266+
# just fit in the width
267+
x = np.linspace(0, w, N)
268+
# and go top-to-bottom
269+
y = np.ones(N) * h
270+
y[::2] = 0
271+
272+
idt = IdentityTransform()
273+
# make a renderer
274+
ra = RendererAgg(w, h, dpi)
275+
# setup the minimal gc to draw a line
276+
gc = ra.new_gc()
277+
gc.set_linewidth(1)
278+
gc.set_foreground('r')
279+
# make a Path
280+
p = Path(np.vstack((x, y)).T)
281+
# effectively disable path simplification (but leaving it "on")
282+
p.simplify_threshold = 0
283+
284+
return ra, gc, p, idt
285+
286+
287+
def test_chunksize_hatch_fail(chunk_limit_setup):
288+
ra, gc, p, idt = chunk_limit_setup
289+
290+
gc.set_hatch('/')
291+
292+
with pytest.raises(OverflowError, match='hatched path'):
293+
ra.draw_path(gc, p, idt)
294+
295+
296+
def test_chunksize_rgbFace_fail(chunk_limit_setup):
297+
ra, gc, p, idt = chunk_limit_setup
298+
299+
with pytest.raises(OverflowError, match='filled path'):
300+
ra.draw_path(gc, p, idt, (1, 0, 0))
301+
302+
303+
def test_chunksize_no_simplify_fail(chunk_limit_setup):
304+
ra, gc, p, idt = chunk_limit_setup
305+
p.should_simplify = False
306+
with pytest.raises(OverflowError, match="should_simplify is False"):
307+
ra.draw_path(gc, p, idt)
308+
309+
310+
def test_chunksize_zero(chunk_limit_setup):
311+
ra, gc, p, idt = chunk_limit_setup
312+
# set to zero to disable, currently defaults to 0, but lets be sure
313+
rcParams['agg.path.chunksize'] = 0
314+
with pytest.raises(OverflowError, match='Please set'):
315+
ra.draw_path(gc, p, idt)
316+
317+
318+
def test_chunksize_too_big_to_chunk(chunk_limit_setup):
319+
ra, gc, p, idt = chunk_limit_setup
320+
# set big enough that we do not try to chunk
321+
rcParams['agg.path.chunksize'] = 1_000_000
322+
with pytest.raises(OverflowError, match='Please reduce'):
323+
ra.draw_path(gc, p, idt)
324+
325+
326+
def test_chunksize_toobig_chunks(chunk_limit_setup):
327+
ra, gc, p, idt = chunk_limit_setup
328+
# small enough we will try to chunk, but big enough we will fail
329+
# to render
330+
rcParams['agg.path.chunksize'] = 90_000
331+
with pytest.raises(OverflowError, match='Please reduce'):
332+
ra.draw_path(gc, p, idt)

lib/matplotlib/tests/test_simplification.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -305,8 +305,8 @@ def test_start_with_moveto():
305305

306306
def test_throw_rendering_complexity_exceeded():
307307
plt.rcParams['path.simplify'] = False
308-
xx = np.arange(200000)
309-
yy = np.random.rand(200000)
308+
xx = np.arange(2_000_000)
309+
yy = np.random.rand(2_000_000)
310310
yy[1000] = np.nan
311311

312312
fig, ax = plt.subplots()

src/_backend_agg.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ RendererAgg::RendererAgg(unsigned int width, unsigned int height, double dpi)
4545
rendererBase(),
4646
rendererAA(),
4747
rendererBin(),
48-
theRasterizer(8192),
48+
theRasterizer(32768),
4949
lastclippath(NULL),
5050
_fill_color(agg::rgba(1, 1, 1, 0))
5151
{

0 commit comments

Comments
 (0)