Title: [Bug]: PDF backend culls visible `hexbin` cells after path collection optimization · Issue #31999 · matplotlib/matplotlib · GitHub
Open Graph Title: [Bug]: PDF backend culls visible `hexbin` cells after path collection optimization · Issue #31999 · matplotlib/matplotlib
X Title: [Bug]: PDF backend culls visible `hexbin` cells after path collection optimization · Issue #31999 · matplotlib/matplotlib
Description: Bug summary Matplotlib 3.11.0 introduced PDF backend culling for path collections to avoid very large PDFs when many colored scatter markers are outside the visible canvas (#30746). This works for scatter, but it incorrectly clips hexbin...
Open Graph Description: Bug summary Matplotlib 3.11.0 introduced PDF backend culling for path collections to avoid very large PDFs when many colored scatter markers are outside the visible canvas (#30746). This works for ...
X Description: Bug summary Matplotlib 3.11.0 introduced PDF backend culling for path collections to avoid very large PDFs when many colored scatter markers are outside the visible canvas (#30746). This works for ...
Opengraph URL: https://github.com/matplotlib/matplotlib/issues/31999
X: @github
Domain: github.com
{"@context":"https://schema.org","@type":"DiscussionForumPosting","headline":"[Bug]: PDF backend culls visible `hexbin` cells after path collection optimization","articleBody":"### Bug summary\n\nMatplotlib 3.11.0 introduced PDF backend culling for path collections to avoid very large PDFs when many colored `scatter` markers are outside the visible canvas (https://github.com/matplotlib/matplotlib/pull/30746). This works for `scatter`, but it incorrectly clips `hexbin` output when the `hexbin` offsets include negative coordinates.\n\n### Code for reproduction\n\n```Python\nimport matplotlib.pyplot as plt\nimport numpy as np\n\nrng = np.random.default_rng(19680801)\nx = rng.normal(size=10_000)\ny = rng.normal(size=10_000)\n\nfig, ax = plt.subplots()\nax.hexbin(x, y, gridsize=25, edgecolors=\"none\")\nfig.savefig(\"hexbin.png\")\nfig.savefig(\"hexbin.pdf\")\n```\n\n### Actual outcome\n\nThe above code produces [hexbin.pdf](https://github.com/user-attachments/files/29671979/hexbin.pdf). Converting it to PNG with `magick` shows: \u003cimg width=\"461\" height=\"346\" alt=\"Image\" src=\"https://github.com/user-attachments/assets/630c2a42-faed-49b3-8a6d-fb7888da0793\" /\u003e\n\n### Expected outcome\n\nThe above happens only for the PDF backend. For the PNG format, it is rendered as expected as below. \u003cimg width=\"640\" height=\"480\" alt=\"Image\" src=\"https://github.com/user-attachments/assets/7bc83942-522f-4d5a-9159-5aeafe79f531\" /\u003e\n\n### Additional information\n\nThe key difference from `scatter` is how the collections are transformed. For `scatter`, the collection offsets transformed by `offset_transform` are the actual marker centers in canvas/display coordinates, and the marker path is centered around that offset. Therefore, checking whether the offset plus marker extent intersects the PDF canvas is valid.\n\nFor `hexbin`, the collection is built from one reusable hexagon path plus many offsets, but the offsets are transformed with `AffineDeltaTransform(self.transData)`. That transform intentionally removes the translation component from `transData`, so the transformed offsets are displacement vectors, not full canvas-space marker centers. The missing translation is carried by the transformed prototype path. The PDF backend culling logic treated those displacement offsets as if they were absolute canvas positions, so visible hexagons with negative offsets were skipped. This makes the PDF output lose part of the `hexbin` plot, while raster backends such as Agg/PNG render correctly.\n\nThis issue can be fixed, with keeping the PR #30746 optimization, but by culling using the final translated path bounds rather than assuming the transformed offset alone is the marker center. This preserves the `scatter` PDF size optimization while correctly handling `hexbin` and any other path collection whose offset transform is not an absolute canvas-position transform.\n\n### Operating system\n\nmacOS Sequoia 15.6\n\n### Matplotlib Version\n\n3.11.0\n\n### Matplotlib Backend\n\nPDF\n\n### Python version\n\n3.13\n\n### Jupyter version\n\n_No response_\n\n### Installation\n\nNone","author":{"url":"https://github.com/yuzie007","@type":"Person","name":"yuzie007"},"datePublished":"2026-07-05T09:41:33.000Z","interactionStatistic":{"@type":"InteractionCounter","interactionType":"https://schema.org/CommentAction","userInteractionCount":0},"url":"https://github.com/31999/matplotlib/issues/31999"}
| route-pattern | /_view_fragments/issues/show/:user_id/:repository/:id/issue_layout(.:format) |
| route-controller | voltron_issues_fragments |
| route-action | issue_layout |
| fetch-nonce | v2:da5abfa4-904a-59bc-5be9-1f9b276f66e7 |
| current-catalog-service-hash | 81bb79d38c15960b92d99bca9288a9108c7a47b18f2423d0f6438c5b7bcd2114 |
| request-id | E2BC:5BE42:25E478:352297:6A4E67A3 |
| html-safe-nonce | 65927495d22d31cd3c1f7eaf7f897f43414d90ee71fb2037a511be039aee1068 |
| visitor-payload | eyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiJFMkJDOjVCRTQyOjI1RTQ3ODozNTIyOTc6NkE0RTY3QTMiLCJ2aXNpdG9yX2lkIjoiMzM3MDg1MzE3Mzk0NjIyMzcxIiwicmVnaW9uX2VkZ2UiOiJpYWQiLCJyZWdpb25fcmVuZGVyIjoiaWFkIn0= |
| visitor-hmac | 3c8a114b3b4e75f8d418009a5404e4848d94b792058558e17fbfde29efc3a8f0 |
| hovercard-subject-tag | issue:4812347414 |
| github-keyboard-shortcuts | repository,issues,copilot |
| google-site-verification | Apib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I |
| octolytics-url | https://collector.github.com/github/collect |
| analytics-location | / |
| fb:app_id | 1401488693436528 |
| apple-itunes-app | app-id=1477376905, app-argument=https://github.com/_view_fragments/issues/show/matplotlib/matplotlib/31999/issue_layout |
| twitter:image | https://opengraph.githubassets.com/31afa061f30146525d71684e9bd89a112ae82c4122bcf7b48f1303c6a714a2d0/matplotlib/matplotlib/issues/31999 |
| twitter:card | summary_large_image |
| og:image | https://opengraph.githubassets.com/31afa061f30146525d71684e9bd89a112ae82c4122bcf7b48f1303c6a714a2d0/matplotlib/matplotlib/issues/31999 |
| og:image:alt | Bug summary Matplotlib 3.11.0 introduced PDF backend culling for path collections to avoid very large PDFs when many colored scatter markers are outside the visible canvas (#30746). This works for ... |
| og:image:width | 1200 |
| og:image:height | 600 |
| og:site_name | GitHub |
| og:type | object |
| og:author:username | yuzie007 |
| hostname | github.com |
| expected-hostname | github.com |
| None | 41b6ab3ba6d20a71766ac245b5a4a94c6fc672a9cd4da7d44c1b33ab8bf6a21c |
| turbo-cache-control | no-preview |
| go-import | github.com/matplotlib/matplotlib git https://github.com/matplotlib/matplotlib.git |
| octolytics-dimension-user_id | 215947 |
| octolytics-dimension-user_login | matplotlib |
| octolytics-dimension-repository_id | 1385122 |
| octolytics-dimension-repository_nwo | matplotlib/matplotlib |
| octolytics-dimension-repository_public | true |
| octolytics-dimension-repository_is_fork | false |
| octolytics-dimension-repository_network_root_id | 1385122 |
| octolytics-dimension-repository_network_root_nwo | matplotlib/matplotlib |
| turbo-body-classes | logged-out env-production page-responsive |
| disable-turbo | false |
| browser-stats-url | https://api.github.com/_private/browser/stats |
| browser-errors-url | https://api.github.com/_private/browser/errors |
| release | e6a744804e8e70f97b4d5a18a94dcc63db22f97a |
| ui-target | full |
| theme-color | #1e2327 |
| color-scheme | light dark |
Links:
Viewport: width=device-width