Skip to content

Commit 6d700a9

Browse files
authored
Honor patch linewidth rc for edgefix (#649)
Preserve rc['patch.linewidth'] for patch-style 2D artists when edgefix is active, and add targeted regressions for bar, hist, pie, and fill_between while leaving collection edgefix behavior unchanged.\n\nCloses #648
1 parent 3f5a15e commit 6d700a9

File tree

2 files changed

+62
-7
lines changed

2 files changed

+62
-7
lines changed

ultraplot/axes/plot.py

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3509,7 +3509,9 @@ def _fix_sticky_edges(self, objs, axis, *args, only=None):
35093509
edges.extend(convert((min_, max_)))
35103510

35113511
@staticmethod
3512-
def _fix_patch_edges(obj, edgefix=None, **kwargs):
3512+
def _fix_patch_edges(
3513+
obj, edgefix=None, default_linewidth: float | None = None, **kwargs
3514+
):
35133515
"""
35143516
Fix white lines between between filled patches and fix issues
35153517
with colormaps that are transparent. If keyword args passed by user
@@ -3520,7 +3522,11 @@ def _fix_patch_edges(obj, edgefix=None, **kwargs):
35203522
# See: https://github.com/jklymak/contourfIssues
35213523
# See: https://stackoverflow.com/q/15003353/4970632
35223524
edgefix = _not_none(edgefix, rc.edgefix, True)
3523-
linewidth = EDGEWIDTH if edgefix is True else 0 if edgefix is False else edgefix
3525+
linewidth = (
3526+
_not_none(default_linewidth, EDGEWIDTH)
3527+
if edgefix is True
3528+
else 0 if edgefix is False else edgefix
3529+
)
35243530
if not linewidth:
35253531
return
35263532
keys = ("linewidth", "linestyle", "edgecolor") # patches and collections
@@ -3557,7 +3563,9 @@ def _fix_patch_edges(obj, edgefix=None, **kwargs):
35573563
obj.set_edgecolor(obj.get_facecolor())
35583564
elif np.iterable(obj): # e.g. silent_list of BarContainer
35593565
for element in obj:
3560-
PlotAxes._fix_patch_edges(element, edgefix=edgefix)
3566+
PlotAxes._fix_patch_edges(
3567+
element, edgefix=edgefix, default_linewidth=default_linewidth
3568+
)
35613569
else:
35623570
warnings._warn_ultraplot(
35633571
f"Unexpected obj {obj} passed to _fix_patch_edges."
@@ -5756,7 +5764,9 @@ def _apply_fill(
57565764
# No synthetic tagging or seaborn-based label overrides
57575765

57585766
# Patch edge fixes
5759-
self._fix_patch_edges(obj, **edgefix_kw, **kw)
5767+
self._fix_patch_edges(
5768+
obj, default_linewidth=rc["patch.linewidth"], **edgefix_kw, **kw
5769+
)
57605770

57615771
# Track sides for sticky edges
57625772
xsides.append(x)
@@ -6039,7 +6049,9 @@ def _apply_bar(
60396049
if isinstance(obj, mcontainer.BarContainer):
60406050
self._add_bar_labels(obj, orientation=orientation, **bar_labels_kw)
60416051

6042-
self._fix_patch_edges(obj, **edgefix_kw, **kw)
6052+
self._fix_patch_edges(
6053+
obj, default_linewidth=rc["patch.linewidth"], **edgefix_kw, **kw
6054+
)
60436055
for y in (b, b + h):
60446056
self._inbounds_xylim(extents, x, y, orientation=orientation)
60456057

@@ -6162,7 +6174,9 @@ def pie(self, x, explode, *, labelpad=None, labeldistance=None, **kwargs):
61626174
**kw,
61636175
)
61646176
objs = tuple(cbook.silent_list(type(seq[0]).__name__, seq) for seq in objs)
6165-
self._fix_patch_edges(objs[0], **edgefix_kw, **wedge_kw)
6177+
self._fix_patch_edges(
6178+
objs[0], default_linewidth=rc["patch.linewidth"], **edgefix_kw, **wedge_kw
6179+
)
61666180
return objs
61676181

61686182
@staticmethod
@@ -7074,7 +7088,9 @@ def _apply_hist(
70747088
kw = self._parse_cycle(n, **kw)
70757089
obj = self._call_native("hist", xs, orientation=orientation, **kw)
70767090
if histtype.startswith("bar"):
7077-
self._fix_patch_edges(obj[2], **edgefix_kw, **kw)
7091+
self._fix_patch_edges(
7092+
obj[2], default_linewidth=rc["patch.linewidth"], **edgefix_kw, **kw
7093+
)
70787094
# Revert to mpl < 3.3 behavior where silent_list was always returned for
70797095
# non-bar-type histograms. Because consistency.
70807096
res = obj[2]

ultraplot/tests/test_plot.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,45 @@ def test_error_shading_explicit_label_external():
9999
assert "Band" in labels
100100

101101

102+
def test_patch_linewidth_rc_controls_patch_edgefix() -> None:
103+
"""
104+
Patch-style artists should honor rc patch linewidth even when edge-fix is active.
105+
"""
106+
expected = 3.5
107+
with uplt.rc.context({"patch.linewidth": expected}):
108+
fig, axs = uplt.subplots(ncols=4)
109+
110+
bar = axs[0].bar([1, 2], [3, 4])
111+
fill = axs[1].fill_between([0, 1, 2], [1, 2, 1])
112+
hist = axs[2].hist(np.arange(5))
113+
pie = axs[3].pie([1, 2, 3])
114+
115+
assert [patch.get_linewidth() for patch in bar.patches] == pytest.approx(
116+
[expected, expected]
117+
)
118+
assert np.atleast_1d(fill.get_linewidths()) == pytest.approx([expected])
119+
assert [patch.get_linewidth() for patch in hist[2]] == pytest.approx(
120+
[expected] * len(hist[2])
121+
)
122+
assert [wedge.get_linewidth() for wedge in pie[0]] == pytest.approx(
123+
[expected] * len(pie[0])
124+
)
125+
126+
uplt.close(fig)
127+
128+
129+
def test_patch_linewidth_rc_does_not_override_collection_edgefix() -> None:
130+
"""
131+
Collection-style 2D artists keep their dedicated edge-fix linewidth.
132+
"""
133+
with uplt.rc.context({"patch.linewidth": 3.5}):
134+
fig, ax = uplt.subplots()
135+
mesh = ax.pcolormesh(np.arange(9).reshape(3, 3))
136+
assert np.atleast_1d(mesh.get_linewidth()) == pytest.approx([0.3])
137+
138+
uplt.close(fig)
139+
140+
102141
def test_graph_nodes_kw():
103142
"""Test the graph method by setting keywords for nodes"""
104143
import networkx as nx

0 commit comments

Comments
 (0)