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

Skip to content

Fix PDF path collection culling for hexbin offsets - #32000

Merged
QuLogic merged 2 commits into
matplotlib:mainfrom
yuzie007:fix-pdf-draw-path-collection
Sep 4, 2026
Merged

Fix PDF path collection culling for hexbin offsets#32000
QuLogic merged 2 commits into
matplotlib:mainfrom
yuzie007:fix-pdf-draw-path-collection

Conversation

@yuzie007

@yuzie007 yuzie007 commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

PR summary

This PR fixes a PDF backend regression where visible hexbin cells can be incorrectly skipped in Matplotlib 3.11.0.

Closes #31999.

PR #30746 added path collection culling to the PDF backend to avoid bloated PDFs when many colored scatter markers are fully outside the canvas. That optimization works for scatter, where transformed offsets correspond to marker centers in canvas coordinates. However, hexbin uses a repeated hexagon path with offsets transformed by AffineDeltaTransform(self.transData). Those transformed offsets are displacements, not absolute canvas-space marker
centers.

The PDF backend was culling collections by checking the transformed offset plus an approximate marker extent. For hexbin, this can classify visible cells as off-canvas, especially when offsets are negative, so the PDF output loses part of the plot while raster output renders correctly.

This PR keeps the optimization from #30746, but changes the culling check to use the actual transformed reusable path bounds translated by each offset. This preserves PDF size improvements for truly off-canvas scatter markers while correctly rendering hexbin and other collections whose transformed offsets are not absolute marker centers. This fix make the example in #31999 work as expected.

A PDF regression test for hexbin with negative offsets, compared against an equivalent shifted reference.

AI Disclosure

Agentic AI was employed to identify the origin of the issue, make the original fix, add the original test, and write the original issue and the PR description. The AI suggestions were reviewed and polished by human.

PR checklist

@yuzie007
yuzie007 force-pushed the fix-pdf-draw-path-collection branch from 1cec52b to 3e5af6f Compare July 5, 2026 10:52
@QuLogic QuLogic added this to the v3.11.1 milestone Jul 9, 2026
# Optimization: Fast path for markers with centers inside canvas.
# This avoids the dictionary lookup for the common case where
# markers are visible, improving performance for large scatter plots.
if 0 <= xo <= canvas_width and 0 <= yo <= canvas_height:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible to look at offset_trans condition the fast path on that as well (my suspicion is that this is Identity by default and that is the only safe case).

Alternatively, would it make sense to apply the offset_trans when doing the check?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As far as I understood from RendererBase._iter_collection, x0 and y0 are the coordinates already after application of offset_trans.
In most typical use cases of scatter and hexbin, offset_trans would be transData.

It is anyway probably wrong to refer only to the offset values without seeing the reference path extent, so I feel it may be better to revise this part substantially.

facecolors, edgecolors, linewidths, linestyles,
antialiaseds, urls, offset_position, hatchcolors=hatchcolors):

# Optimization: Fast path for markers with centers inside canvas.

@tacaswell tacaswell Jul 10, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have any benchmarks on what we are giving up performance-wise here?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree on your concern and would like to suggest an alternative approach as follows.

If I understand correctly, the "optimization" here essentially meant to avoid the look-up of the path_extent_map dictionary.
We can actually avoid this look-up in a different viewpoint.
For most use cases of Collection via scatter and hexbin, the paths list has actually only a single element.
In such a case, we do not have to make a dictionary and do not need to look it up.

In the newly added commit, I implement the idea above. This should retain the original "optimization" in the sense of avoiding the dictionary look-up.

@QuLogic QuLogic modified the milestones: v3.11.1, v3.11.2, future releases Jul 17, 2026

@yuzie007 yuzie007 left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you very much @tacaswell for your kind review, and I am so sorry for my late response.

Motivated by your comments, I have added one new commit, which I hope to solve the concerns. Could you review once again when convenient for you and check if what I wrote makes sense or maybe I misunderstand something?

facecolors, edgecolors, linewidths, linestyles,
antialiaseds, urls, offset_position, hatchcolors=hatchcolors):

# Optimization: Fast path for markers with centers inside canvas.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree on your concern and would like to suggest an alternative approach as follows.

If I understand correctly, the "optimization" here essentially meant to avoid the look-up of the path_extent_map dictionary.
We can actually avoid this look-up in a different viewpoint.
For most use cases of Collection via scatter and hexbin, the paths list has actually only a single element.
In such a case, we do not have to make a dictionary and do not need to look it up.

In the newly added commit, I implement the idea above. This should retain the original "optimization" in the sense of avoiding the dictionary look-up.

# Optimization: Fast path for markers with centers inside canvas.
# This avoids the dictionary lookup for the common case where
# markers are visible, improving performance for large scatter plots.
if 0 <= xo <= canvas_width and 0 <= yo <= canvas_height:

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As far as I understood from RendererBase._iter_collection, x0 and y0 are the coordinates already after application of offset_trans.
In most typical use cases of scatter and hexbin, offset_trans would be transData.

It is anyway probably wrong to refer only to the offset values without seeing the reference path extent, so I feel it may be better to revise this part substantially.

Comment thread lib/matplotlib/backends/backend_pdf.py Outdated
# Skip markers completely outside the canvas to reduce PDF size.
# Use the translated path bounds, not the offset alone: the offset
# need not be the marker center in canvas coordinates.
bbox = path_extent_map[path_id] if path_extent_map else single_path_extent

@tacaswell tacaswell Sep 4, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a very odd construction and I'm not sure is actually saving us anything.

The previous logic was "is the offset in the canvas? if so definitely try to draw the path, if not check if any of its extents fall in"

I suppose that from some very pathological paths which are effectively "far" from the origin that this is not a good heuristic, but I do no beleive that is common.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah, I get it now. The core problem is that x0 and y0 are not in the coordinate system we expected.

@tacaswell
tacaswell force-pushed the fix-pdf-draw-path-collection branch from 8ffabe7 to 3e5af6f Compare September 4, 2026 19:02

@tacaswell tacaswell left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I pushed the last commit away, I think it made it significantly more confusing and we will deal with the performance regression later if needed.

@QuLogic
QuLogic merged commit a7db543 into matplotlib:main Sep 4, 2026
68 checks passed
QuLogic added a commit that referenced this pull request Sep 5, 2026
…000-on-v3.11.x

Backport PR #32000 on branch v3.11.x (Fix PDF path collection culling for `hexbin` offsets)
@yuzie007

yuzie007 commented Sep 5, 2026

Copy link
Copy Markdown
Contributor Author

Thank you @tacaswell for the update and merging the PR! I agree that the last added commit may be functionality-wise unnecessary and fully agree on removing it.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: PDF backend culls visible hexbin cells after path collection optimization

4 participants