Skip to content

Add highlight marker support for plot/scatter API in SVG backend#31345

Open
wuyao1997 wants to merge 9 commits intomatplotlib:mainfrom
wuyao1997:main
Open

Add highlight marker support for plot/scatter API in SVG backend#31345
wuyao1997 wants to merge 9 commits intomatplotlib:mainfrom
wuyao1997:main

Conversation

@wuyao1997
Copy link

@wuyao1997 wuyao1997 commented Mar 21, 2026

This PR addresses the long-standing feature request for gradient/highlight-styled markers in Matplotlib's plot and scatter APIs, as raised in #8926. Currently, Matplotlib lacks native support for glossy/highlighted marker rendering across backends, with workarounds like post-processing SVG files or using custom images being the only options—both of which have limitations with legend compatibility and ease of use.

Implementation Method

This change implements basic highlight marker functionality exclusively for the SVG backend by introducing a dynamic _highlight_svg member injected into Line2D (for plot) and PathCollection (for scatter) classes. The draw methods of these two classes are modified to check for the _highlight_svg flag, and the RendererSVG.draw_markers method is updated to render a highlight mask over markers when the flag is set, achieving a glossy highlight effect.

Extensibility

The implementation is intentionally minimal and unrefined at this stage, but the core design is backend-agnostic and extensible: the same flag-based injection pattern can be adapted to other backends (e.g., Agg) with backend-specific rendering logic. For the Agg backend, this would require modifications to the underlying C language code for marker drawing, which is left as future work.

Minimum self-contained example

main

import matplotlib.pyplot as plt
import numpy as np


x1 = [1, 2, 3, 4]
y1 = np.ones_like(x1)
np.random.seed(19680801)
x2 = np.linspace(1, 4, 11, endpoint=True)
y2 = np.random.rand(11) + 2

fig, ax = plt.subplots(figsize=(4, 3))

(line,) = ax.plot(
    x1,
    y1,
    marker="o",
    markersize=10,
    ls="-",
    lw=2,
    label="A: line",
    alpha=1,
    mew=1,
    mec="gray",
    markerfacecolor="dimgray",
    # markerfacecoloralt="gray",
    # fillstyle="left",
)
line._highlight_svg = True

(line2,) = ax.plot(
    x1,
    y1 + 1,
    marker="D",
    markersize=10,
    lw=2,
    color="#6a9f1b",
    mec="gray",
    label="B: line",
    alpha=0.75,
)
line2._highlight_svg = True

pc = ax.scatter(
    x2,
    y2,
    s=100,
    lw=1,
    color="salmon",
    ec="gray",
    label="C: scatter",
)
pc._highlight_svg = True


ax.legend(loc=0, ncol=3, frameon=True, bbox_to_anchor=(0, 0.2, 1, 0.2))
ax.set_title("highlight marker for scatter and line plot\n(SVG Backend)")

plt.savefig("main.svg", dpi=144)
plt.savefig("main.png", dpi=144) # no effect
plt.savefig("main.svg.xml", format="svg", dpi=144)
plt.show() # no effect

Known issues

A fixed element ID - _m_overlayGradient is used in the SVG backend, and this content is written to the SVG file regardless of whether the image contains highlighted markers or not.

Closes #8926

AI Disclosure

No generative AI tools were used in the development of this PR. All code and documentation were written manually with reference to Matplotlib's existing backend and marker rendering codebase.

PR checklist

Remove unnecessary blank line in draw_markers function.
@timhoffm
Copy link
Member

Thanks for the PR. However, we must handle such a change more systematically. Adding highlight info to draw_marker is an API change. From a quick look, you seem to have made this backward-compatible, which is necessary because there are third party backends, so that we cannot simply globally update the signature to draw_marker. However, that comes at the cost of very dynamic code in multiple places, that should conceptually not have to bother with these details.

For maintainability, I'm not willing to accept hasattr(..., "_highlight_svg") or the blocks containing from .backends.backend_svg import RendererSVG.

Under these constraints, the problem is much harder. A solution strategy would be

  • Define "highlight" on the marker level. This could be a property of MarkerStyle (if it would make sense to have this for all markers) or dedicated new markers that use this.
  • Expand the draw_markers API, so that there is an official way how to do highlighting, but it is still backwards compatible. This is possible but quite a song-and-dance. We've done this once in https://github.com/matplotlib/matplotlib/pull/29044/changes#diff-0563f83260b79acbd260c8f0207b01ba8182df37bf2ccd9216d9a3c6a70cbf29, but this approach does not scale. We have just introduced backend versioning [ENH]: Backend versioning #30559 to have a way to consistently talk about API versions. We do not have sustainable update or migration strategies, see the discussions linked therein.
  • Implement the added optional highlight API in the SVG backend.

While this is doable, the first two points are quite a heavy lift, and I'm inclined to argue that highlight markers are quite rare and not worth the effort.

What would me more valuable, would be a general gradient support, but that's an even more complex topic. You have the above issue, plus a more complex specification at the artist level.

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.

Maker with gradient filling for plot and scatter [feature request]

2 participants