Skip to content

fix(points): render solid markers at small sizes - #757

Merged
timtreis merged 3 commits into
mainfrom
fix/issue-756
Aug 7, 2026
Merged

fix(points): render solid markers at small sizes#757
timtreis merged 3 commits into
mainfrom
fix/issue-756

Conversation

@timtreis

@timtreis timtreis commented Aug 6, 2026

Copy link
Copy Markdown
Member

Problem

Closes #756. render_points(method="matplotlib") with a small size at high dpi renders hollow "outlined" circles instead of filled dots.

Root cause

_scatter_points called ax.scatter without edge parameters, so matplotlib's defaults applied: edgecolors="face" and linewidths=1.0 (points). matplotlib strokes the "o" marker path with that fixed points-unit linewidth, and the stroke of a circle is an annulus. When the fill disk goes sub-pixel (tiny size, high dpi) only the ring survives → the marker renders as a hollow outline.

Confirmed empirically (saturation grid at size=0.1, dpi=300): bright outer ring, hollow interior valley, i.e. a donut.

Fix

Add linewidths=0 to the shared ax.scatter call in _scatter_points. Markers stay solid fills at every size, and size now maps honestly to the fill area (points²), aligning the matplotlib backend with datashader. The change also covers the shapes/labels as_points centroid fast-mode, which shares this primitive.

@timtreis timtreis changed the title fix(points): render solid markers at small sizes (#756) fix(points): render solid markers at small sizes Aug 6, 2026
matplotlib's default scatter edge stroke (edgecolors="face",
linewidths=1pt) strokes each circle marker with a fixed points-unit
ring. When the fill disk goes sub-pixel (tiny `size` at high dpi) only
that ring survives, so markers render as hollow "outlined" circles.

Set linewidths=0 in _scatter_points so markers stay solid fills at every
size; `size` now maps honestly to the fill area. Also removes the
same-color ~1pt halo that silently enlarged every marker, so point
markers render slightly smaller (visual baselines need regenerating).

Fixes #756.
Add an explicit "import matplotlib.collections" instead of relying on
pyplot's transitive import, and document that the pixel-level solid-disk
appearance is covered by the CI visual baselines while this non-visual
test guards the mechanism.
Markers now render as solid fills without the default 1pt edge stroke,
so they are slightly smaller and match the datashader backend. Baselines
regenerated from the py3.12-stable CI artifact of run 31130393033.
@codecov-commenter

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 79.60%. Comparing base (227434d) to head (699a275).
⚠️ Report is 8 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main     #757   +/-   ##
=======================================
  Coverage   79.59%   79.60%           
=======================================
  Files          17       18    +1     
  Lines        4641     4657   +16     
  Branches     1029     1032    +3     
=======================================
+ Hits         3694     3707   +13     
- Misses        598      599    +1     
- Partials      349      351    +2     
Files with missing lines Coverage Δ
src/spatialdata_plot/pl/render.py 89.41% <ø> (-0.21%) ⬇️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@timtreis
timtreis merged commit 5627c9b into main Aug 7, 2026
7 of 8 checks passed
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.

render_points() renders outlined circles for small point sizes

2 participants