Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions js/src/10_colormaps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@

const COLORMAP_STOPS = {
binary: [[255, 255, 255], [0, 0, 0]],
reds: [[255, 245, 240], [254, 229, 216], [253, 202, 181], [252, 171, 143], [252, 138, 106], [251, 105, 74], [241, 68, 50], [217, 37, 35], [188, 20, 26], [152, 12, 19], [103, 0, 13]],
bone: [[0, 0, 0], [22, 22, 30], [45, 45, 62], [66, 66, 93], [89, 92, 121], [112, 123, 144], [134, 154, 166], [157, 185, 188], [185, 210, 210], [221, 233, 233], [255, 255, 255]],
autumn: [[255, 0, 0], [255, 25, 0], [255, 51, 0], [255, 76, 0], [255, 102, 0], [255, 128, 0], [255, 153, 0], [255, 179, 0], [255, 204, 0], [255, 230, 0], [255, 255, 0]],
winter: [[0, 0, 255], [0, 25, 242], [0, 51, 230], [0, 76, 217], [0, 102, 204], [0, 128, 191], [0, 153, 178], [0, 179, 166], [0, 204, 153], [0, 230, 140], [0, 255, 128]],
bupu: [[247, 252, 253], [229, 239, 246], [204, 221, 236], [178, 202, 225], [154, 180, 214], [140, 149, 198], [140, 116, 181], [138, 81, 165], [133, 45, 144], [118, 12, 113], [77, 0, 75]],
gray: [[0, 0, 0], [25, 25, 25], [51, 51, 51], [76, 76, 76], [102, 102, 102], [128, 128, 128], [153, 153, 153], [179, 179, 179], [204, 204, 204], [230, 230, 230], [255, 255, 255]],
viridis: [[68, 1, 84], [72, 36, 117], [65, 68, 135], [53, 95, 141], [42, 120, 142], [33, 145, 140], [34, 168, 132], [68, 191, 112], [122, 209, 81], [189, 223, 38], [253, 231, 37]],
plasma: [[13, 8, 135], [65, 4, 157], [106, 0, 168], [143, 13, 164], [177, 42, 144], [204, 71, 120], [225, 100, 98], [242, 132, 75], [252, 166, 54], [252, 206, 37], [240, 249, 33]],
Expand Down
65 changes: 65 additions & 0 deletions python/xy/_svg.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,71 @@ def _stroke_opacity(style: dict[str, Any], default: float = 1.0) -> float:
# Mirrors js/src/10_colormaps.ts COLORMAP_STOPS (§36) — test-guarded.
COLORMAP_STOPS: dict[str, list[tuple[int, int, int]]] = {
"binary": [(255, 255, 255), (0, 0, 0)],
"reds": [
(255, 245, 240),
(254, 229, 216),
(253, 202, 181),
(252, 171, 143),
(252, 138, 106),
(251, 105, 74),
(241, 68, 50),
(217, 37, 35),
(188, 20, 26),
(152, 12, 19),
(103, 0, 13),
],
"bone": [
(0, 0, 0),
(22, 22, 30),
(45, 45, 62),
(66, 66, 93),
(89, 92, 121),
(112, 123, 144),
(134, 154, 166),
(157, 185, 188),
(185, 210, 210),
(221, 233, 233),
(255, 255, 255),
],
"autumn": [
(255, 0, 0),
(255, 25, 0),
(255, 51, 0),
(255, 76, 0),
(255, 102, 0),
(255, 128, 0),
(255, 153, 0),
(255, 179, 0),
(255, 204, 0),
(255, 230, 0),
(255, 255, 0),
],
"winter": [
(0, 0, 255),
(0, 25, 242),
(0, 51, 230),
(0, 76, 217),
(0, 102, 204),
(0, 128, 191),
(0, 153, 178),
(0, 179, 166),
(0, 204, 153),
(0, 230, 140),
(0, 255, 128),
],
"bupu": [
(247, 252, 253),
(229, 239, 246),
(204, 221, 236),
(178, 202, 225),
(154, 180, 214),
(140, 149, 198),
(140, 116, 181),
(138, 81, 165),
(133, 45, 144),
(118, 12, 113),
(77, 0, 75),
],
"gray": [
(0, 0, 0),
(25, 25, 25),
Expand Down
5 changes: 5 additions & 0 deletions python/xy/channels.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@
"rdbu",
"jet",
"binary",
"reds",
"bone",
"autumn",
"winter",
"bupu",
)


Expand Down
8 changes: 6 additions & 2 deletions python/xy/pyplot/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2833,12 +2833,16 @@ def get_cmap(name: str | None = None, lut: int | None = None) -> Cmap:
return get_cmap(name, lut)

def __getattr__(self, name: str) -> Any:
from ._colors import CMAPS
from ._colors import resolve_cmap

if name == "ScalarMappable":
return type("ScalarMappable", (), {"__init__": lambda self, **kwargs: None})

if name.lower() in CMAPS:
try:
resolve_cmap(name)
except ValueError:
pass
else:
return name
raise AttributeError(f"colormap {name!r} is not supported; see spec/matplotlib/compat.md")

Expand Down
5 changes: 5 additions & 0 deletions python/xy/pyplot/_colors.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,11 @@ def scalar_float(value: Any) -> float:
"rdgy": "rdgy",
"jet": "jet",
"binary": "binary",
"reds": "reds",
"bone": "bone",
"autumn": "autumn",
"winter": "winter",
"bupu": "bupu",
}


Expand Down
2 changes: 1 addition & 1 deletion spec/matplotlib/compat.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ dependency-free `triangles=` shorthand into Matplotlib's equivalent
| `plt.show()` | notebooks: inline HTML display; scripts: opens the default browser |
| Artists: `set_data` / `set_ydata` / `set_color` / `set_label` / `set_linewidth` / `remove` | mutating a handle rebuilds the chart on next render. Scatter collections additionally vectorize facecolors, edgecolors, alpha, linewidths, and sizes; `alpha=None` restores intrinsic paint alpha |
| Colors | single letters, `C0`–`C9`, `tab:*`, gray `'0.5'`, RGB(A) tuples, `(color, alpha)` pairs, per-item RGB(A) arrays, and any CSS color |
| `plt.cm.*` / `plt.colormaps[...]` / `cmap=` names | viridis, plasma, inferno, magma, cividis, gray, turbo, coolwarm, Blues, Purples, PuBu, RdBu, RdYlGn, RdGy, PiYG, PRGn, jet, rainbow, Spectral, binary, aliases, and true `*_r` reversal (RdGy/jet render from 11-stop anchor tables sampled from Matplotlib 3.11, linearly interpolated) |
| `plt.cm.*` / `plt.colormaps[...]` / `cmap=` names | viridis, plasma, inferno, magma, cividis, gray, bone, autumn, winter, turbo, coolwarm, Blues, Purples, Reds, PuBu, BuPu, RdBu, RdYlGn, RdGy, PiYG, PRGn, jet, rainbow, Spectral, binary, aliases, and true `*_r` reversal resolved generically for every listed name, including `plt.cm.<name>_r` attribute access (RdGy/jet/Reds/bone/autumn/winter/BuPu render from 11-stop anchor tables sampled from Matplotlib 3.11, linearly interpolated) |
| `LinearSegmentedColormap.from_list` / `ListedColormap` | Python-side callables (`cmap(np.arange(cmap.N))` → RGBA) for scripts that colormap values themselves; they cannot be passed as `cmap=` to plotting calls (no engine table), which fails loudly |
| `plt.colorbar()` / `fig.colorbar()` / `plt.clim()` / `plt.gci()` | Returns a live handle (`set_label`, `set_ticks`); with no mappable it uses the current image the way pyplot does. `ticks=`/`extend=` render in PNG and SVG (the HTML colorbar stays a minimal gradient without tick text); `clim` retargets the mappable's color window and any colorbar derived from it |
| `rcParams` | Figure size/DPI, line width/marker size, image cmap/origin, axes color cycle, and all four `axes.spines.*` switches affect every exporter. Pyplot axes default to Matplotlib's four-sided box and each spine can be hidden independently. The chrome keys (axes face/edge/label/title styles, font family/size, tick colors/sizes, legend defaults, figure facecolor) reach the HTML renderer and multi-panel PNG stitching; single-chart PNG and SVG export currently render their own fixed chrome and ignore them. Unknown keys warn once |
Expand Down
19 changes: 19 additions & 0 deletions tests/pyplot/test_pdsh_gap_features.py
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,25 @@ def test_rdgy_and_jet_resolve_and_render():
_png()


@pytest.mark.parametrize(
("name", "canonical"),
[
("Reds_r", "reds_r"),
("bone", "bone"),
("autumn", "autumn"),
("winter", "winter"),
("BuPu", "bupu"),
],
)
def test_matplotlib_gallery_colormaps_resolve_and_render(name, canonical):
assert plt.get_cmap(name).name == canonical
assert plt.colormaps[name].name == canonical
assert getattr(plt.cm, name) == name
fig, ax = plt.subplots()
ax.imshow(np.arange(16.0).reshape(4, 4), cmap=name)
_png(fig)


def test_linear_segmented_colormap_from_list_matches_anchors():
table = np.array([[0.0, 0.0, 0.0, 1.0], [1.0, 1.0, 1.0, 1.0]])
cmap = plt.LinearSegmentedColormap.from_list("g", table, 8)
Expand Down
81 changes: 80 additions & 1 deletion tests/test_svg_export.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@
import pytest

import xy
from xy import channels
from xy._figure import Figure
from xy._svg import COLORMAP_STOPS, _axis_tick_label_layout, _Scale
from xy._svg import COLORMAP_STOPS, _axis_tick_label_layout, _colormap_stops, _Scale

ROOT = Path(__file__).resolve().parents[1]

Expand Down Expand Up @@ -684,6 +685,84 @@ def test_colormap_stops_stay_in_sync_with_js_client() -> None:
assert f"[{r}, {g}, {b}]" in body, (
f"{name} stop ({r},{g},{b}) missing in 10_colormaps.ts"
)
assert set(COLORMAP_STOPS) == set(channels.COLORMAPS), (
"renderer and public colormap registries diverged"
)


def test_matplotlib_gallery_colormap_stops_and_reversal() -> None:
expected = {
"reds": [
(255, 245, 240),
(254, 229, 216),
(253, 202, 181),
(252, 171, 143),
(252, 138, 106),
(251, 105, 74),
(241, 68, 50),
(217, 37, 35),
(188, 20, 26),
(152, 12, 19),
(103, 0, 13),
],
"bone": [
(0, 0, 0),
(22, 22, 30),
(45, 45, 62),
(66, 66, 93),
(89, 92, 121),
(112, 123, 144),
(134, 154, 166),
(157, 185, 188),
(185, 210, 210),
(221, 233, 233),
(255, 255, 255),
],
"autumn": [
(255, 0, 0),
(255, 25, 0),
(255, 51, 0),
(255, 76, 0),
(255, 102, 0),
(255, 128, 0),
(255, 153, 0),
(255, 179, 0),
(255, 204, 0),
(255, 230, 0),
(255, 255, 0),
],
"winter": [
(0, 0, 255),
(0, 25, 242),
(0, 51, 230),
(0, 76, 217),
(0, 102, 204),
(0, 128, 191),
(0, 153, 178),
(0, 179, 166),
(0, 204, 153),
(0, 230, 140),
(0, 255, 128),
],
"bupu": [
(247, 252, 253),
(229, 239, 246),
(204, 221, 236),
(178, 202, 225),
(154, 180, 214),
(140, 149, 198),
(140, 116, 181),
(138, 81, 165),
(133, 45, 144),
(118, 12, 113),
(77, 0, 75),
],
}
for name, stops in expected.items():
assert COLORMAP_STOPS[name] == stops
assert channels.is_colormap(name)
assert channels.is_colormap(f"{name}_r")
assert _colormap_stops(f"{name}_r") == list(reversed(stops))


def test_scalar_stroke_color_survives_vectorized_style_path() -> None:
Expand Down
Loading