◐ Shell
clean mode source ↗

Simplify fill_between paths in SVG and PDF backends by 0x7e5oI · Pull Request #31359 · matplotlib/matplotlib

PR summary

Suggestion for closing #22803.

This PR reduces SVG/PDF output size for fill_between by allowing simplification to be applied to FillBetweenPolyCollection paths when path.simplify is enabled.

Why

plot() already benefits from path simplification because Line2D paths are passed to the backends with the relevant simplification behavior already in place. In contrast, fill_between() produces a FillBetweenPolyCollection, and the resulting collection paths were not consistently reaching the vector backends in a form that allowed the same simplification to happen. its paths are closed polygons (they contain CLOSEPATH codes), which disables should_simplify
by design in Path._update_values. Additionally, filled paths always have clip=False in draw_path, so the existing simplification gate never fired.

In practice, this meant that increasing path.simplify_threshold greatly reduced vector output size for line plots, but often had little or no effect for fill_between().

What does this PR change

  • FillBetweenPolyCollection.set_verts tags each generated path with a _fill_between_simplify attribute.
  • In RendererSVG.draw_path and RendererPDF.draw_path, paths carrying that attribute (and without a hatch) are simplified by checking rcParams["path.simplify"] and rcParams["path.simplify_threshold"] directly, bypassing the should_simplifyproperty which is alwaysFalse` for closed polygon paths.
  • Hatched fill_between paths are explicitly excluded: in SVG, the hatch pattern is clipped to the fill polygon boundary, so simplifying that boundary would change the visible output.
  • No changes to path collection optimization or to unmarked paths - existing behavior is fully preserved.

What this approach solves:

For simplifiable fill_between paths, exported SVG/PDF files are reduced substantially. In the strongest benchmarked case (interpolate_cross), output size dropped by about 79.7% abd 75.5% for SVG and PDF, respectively. More fragmented cases still showed reductions, though with smaller gains and more mixed timing results.

Implementation notes:

-the change is intentionally narrow in scoppe
-simplification is only enabled for paths explicitly marked as simplifiable, rather than broadening simplification for all collections
-the PDF and SVG backends keep their existing behavior for unmarked paths
-the tests focus on output-size reduction rather than exact rendering bytes

Opt-in

Simplification only fires when the user has already set rcParams["path.simplify"] = True and rcParams["path.simplify_threshold"] > 0. This matches how simplification works for line plots in the same backends.

Scenario Backend thr=0.0 thr=1.0 Reduction
interpolate_cross SVG 73,531 14,943 79.7%
interpolate_cross PDF 30,102 7,382 75.5%
where_random SVG 155,694 104,644 32.8%
where_random PDF 35,829 20,425 43.0%
multi_regions SVG 392,895 293,482 25.3%
multi_regions PDF 58,940 33,757 42.7%

The old numbers (99.6-99.7% for interpolate_cross) were from the previous approach with a much larger dataset or different test setup. The real numbers here are more modest but still meaningful (75-80% for the smooth curve case, 25-43% for the fragmented cases).

Test

lib/matplotlib/tests/test_fill_between_simplify.py verifies that SVG and PDF output size is smaller at simplify_threshold=1.0 than at 0.0, for both single and multi-region fill_between scenarios.

Tested on 2000-point datasets. Reduction magnitude depends heavily on data density and threshold value - smooth curves (e.g. interpolate_cross) see near-complete reduction at threshold=1.0, while fragmented where masks
see smaller gains. The regression tests verify that reduction occurs in both backends across representative scenarios.

AI Disclosure

AI was used to assist with debugging environment issues, crafting the new test and refining the PR description, all the rest being manually tested.

Further explanation on the comments.

  • "closes #22803"
  • new and changed code is tested
  • [N/A] Plotting related features are demonstrated
  • [N/A ] New Features and API Changes are noted
  • [N/A] Documentation complies with general and docstring guidelines