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

Skip to content

Commit a7db543

Browse files
authored
Merge pull request #32000 from yuzie007/fix-pdf-draw-path-collection
Fix PDF path collection culling for `hexbin` offsets
2 parents 996cb9a + 3e5af6f commit a7db543

2 files changed

Lines changed: 38 additions & 30 deletions

File tree

lib/matplotlib/backends/backend_pdf.py

Lines changed: 11 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -2043,16 +2043,11 @@ def draw_path_collection(self, gc, master_transform, paths, all_transforms,
20432043
name = self.file.pathCollectionObject(
20442044
gc, path, transform, padding, filled, stroked)
20452045
path_codes.append(name)
2046-
# Compute the extent of each marker path to enable per-marker
2047-
# bounds checking. This allows us to skip markers that are
2048-
# completely outside the visible canvas while preserving markers
2049-
# that are partially visible.
2050-
if len(path.vertices):
2051-
bbox = path.get_extents(transform)
2052-
# Store half-width and half-height for efficient bounds checking
2053-
path_extents.append((bbox.width / 2, bbox.height / 2))
2054-
else:
2055-
path_extents.append((0, 0))
2046+
# Compute each transformed path's exact bounds for per-marker
2047+
# canvas checks. Offsets are not necessarily full canvas-space
2048+
# centers, e.g. for collections using AffineDeltaTransform, so
2049+
# cull based on the final path bounds translated by the offset.
2050+
path_extents.append(path.get_extents(transform).frozen())
20562051

20572052
# Create a mapping from path_id to extent for efficient lookup
20582053
path_extent_map = dict(zip(path_codes, path_extents))
@@ -2068,26 +2063,12 @@ def draw_path_collection(self, gc, master_transform, paths, all_transforms,
20682063
facecolors, edgecolors, linewidths, linestyles,
20692064
antialiaseds, urls, offset_position, hatchcolors=hatchcolors):
20702065

2071-
# Optimization: Fast path for markers with centers inside canvas.
2072-
# This avoids the dictionary lookup for the common case where
2073-
# markers are visible, improving performance for large scatter plots.
2074-
if 0 <= xo <= canvas_width and 0 <= yo <= canvas_height:
2075-
# Marker center is inside canvas - definitely render it
2076-
self.check_gc(gc0, rgbFace)
2077-
dx, dy = xo - lastx, yo - lasty
2078-
output(1, 0, 0, 1, dx, dy, Op.concat_matrix, path_id,
2079-
Op.use_xobject)
2080-
lastx, lasty = xo, yo
2081-
continue
2082-
2083-
# Marker center is outside canvas - check if partially visible.
2084-
# Skip markers completely outside visible canvas bounds to reduce
2085-
# PDF file size. Use per-marker extents to handle large markers
2086-
# correctly: only skip if the marker's bounding box doesn't
2087-
# intersect the canvas at all.
2088-
extent_x, extent_y = path_extent_map[path_id]
2089-
if not (-extent_x <= xo <= canvas_width + extent_x
2090-
and -extent_y <= yo <= canvas_height + extent_y):
2066+
# Skip markers completely outside the canvas to reduce PDF size.
2067+
# Use the translated path bounds, not the offset alone: the offset
2068+
# need not be the marker center in canvas coordinates.
2069+
bbox = path_extent_map[path_id]
2070+
if (bbox.x1 + xo < 0 or bbox.x0 + xo > canvas_width
2071+
or bbox.y1 + yo < 0 or bbox.y0 + yo > canvas_height):
20912072
continue
20922073

20932074
self.check_gc(gc0, rgbFace)

lib/matplotlib/tests/test_backend_pdf.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -732,3 +732,30 @@ def test_scatter_polar(fig_test, fig_ref):
732732
ax_ref = fig_ref.subplots(subplot_kw={'projection': 'polar'})
733733
ax_ref.scatter(theta, r, c=c, s=50)
734734
ax_ref.set_ylim(0, 2)
735+
736+
737+
@check_figures_equal(extensions=["pdf"], tol=1.0)
738+
def test_hexbin_negative_offsets(fig_test, fig_ref):
739+
"""
740+
Test path collection culling with non-canvas-space offsets.
741+
742+
Hexbin uses a repeated polygon path with offsets transformed by
743+
AffineDeltaTransform. The transformed offsets are displacements, not the
744+
final canvas-space marker centers, so PDF backend culling must account for
745+
the transformed path bounds as well.
746+
"""
747+
rng = np.random.default_rng(19680801)
748+
x = rng.normal(size=10_000)
749+
y = rng.normal(size=10_000)
750+
751+
ax_test = fig_test.subplots()
752+
ax_test.hexbin(x, y, gridsize=25, edgecolors="none")
753+
ax_test.set_xlim(-2.0, 2.0)
754+
ax_test.set_ylim(-2.0, 2.0)
755+
ax_test.set_axis_off()
756+
757+
ax_ref = fig_ref.subplots()
758+
ax_ref.hexbin(x + 2.0, y + 2.0, gridsize=25, edgecolors="none")
759+
ax_ref.set_xlim(0.0, 4.0)
760+
ax_ref.set_ylim(0.0, 4.0)
761+
ax_ref.set_axis_off()

0 commit comments

Comments
 (0)