diff --git a/ultraplot/axes/plot.py b/ultraplot/axes/plot.py index 7aacaaa27..3ba71f09f 100644 --- a/ultraplot/axes/plot.py +++ b/ultraplot/axes/plot.py @@ -3509,7 +3509,9 @@ def _fix_sticky_edges(self, objs, axis, *args, only=None): edges.extend(convert((min_, max_))) @staticmethod - def _fix_patch_edges(obj, edgefix=None, **kwargs): + def _fix_patch_edges( + obj, edgefix=None, default_linewidth: float | None = None, **kwargs + ): """ Fix white lines between between filled patches and fix issues with colormaps that are transparent. If keyword args passed by user @@ -3520,7 +3522,11 @@ def _fix_patch_edges(obj, edgefix=None, **kwargs): # See: https://github.com/jklymak/contourfIssues # See: https://stackoverflow.com/q/15003353/4970632 edgefix = _not_none(edgefix, rc.edgefix, True) - linewidth = EDGEWIDTH if edgefix is True else 0 if edgefix is False else edgefix + linewidth = ( + _not_none(default_linewidth, EDGEWIDTH) + if edgefix is True + else 0 if edgefix is False else edgefix + ) if not linewidth: return keys = ("linewidth", "linestyle", "edgecolor") # patches and collections @@ -3557,7 +3563,9 @@ def _fix_patch_edges(obj, edgefix=None, **kwargs): obj.set_edgecolor(obj.get_facecolor()) elif np.iterable(obj): # e.g. silent_list of BarContainer for element in obj: - PlotAxes._fix_patch_edges(element, edgefix=edgefix) + PlotAxes._fix_patch_edges( + element, edgefix=edgefix, default_linewidth=default_linewidth + ) else: warnings._warn_ultraplot( f"Unexpected obj {obj} passed to _fix_patch_edges." @@ -5756,7 +5764,9 @@ def _apply_fill( # No synthetic tagging or seaborn-based label overrides # Patch edge fixes - self._fix_patch_edges(obj, **edgefix_kw, **kw) + self._fix_patch_edges( + obj, default_linewidth=rc["patch.linewidth"], **edgefix_kw, **kw + ) # Track sides for sticky edges xsides.append(x) @@ -6039,7 +6049,9 @@ def _apply_bar( if isinstance(obj, mcontainer.BarContainer): self._add_bar_labels(obj, orientation=orientation, **bar_labels_kw) - self._fix_patch_edges(obj, **edgefix_kw, **kw) + self._fix_patch_edges( + obj, default_linewidth=rc["patch.linewidth"], **edgefix_kw, **kw + ) for y in (b, b + h): self._inbounds_xylim(extents, x, y, orientation=orientation) @@ -6162,7 +6174,9 @@ def pie(self, x, explode, *, labelpad=None, labeldistance=None, **kwargs): **kw, ) objs = tuple(cbook.silent_list(type(seq[0]).__name__, seq) for seq in objs) - self._fix_patch_edges(objs[0], **edgefix_kw, **wedge_kw) + self._fix_patch_edges( + objs[0], default_linewidth=rc["patch.linewidth"], **edgefix_kw, **wedge_kw + ) return objs @staticmethod @@ -7074,7 +7088,9 @@ def _apply_hist( kw = self._parse_cycle(n, **kw) obj = self._call_native("hist", xs, orientation=orientation, **kw) if histtype.startswith("bar"): - self._fix_patch_edges(obj[2], **edgefix_kw, **kw) + self._fix_patch_edges( + obj[2], default_linewidth=rc["patch.linewidth"], **edgefix_kw, **kw + ) # Revert to mpl < 3.3 behavior where silent_list was always returned for # non-bar-type histograms. Because consistency. res = obj[2] diff --git a/ultraplot/tests/test_plot.py b/ultraplot/tests/test_plot.py index 8aabb865d..1f8811ed2 100644 --- a/ultraplot/tests/test_plot.py +++ b/ultraplot/tests/test_plot.py @@ -99,6 +99,45 @@ def test_error_shading_explicit_label_external(): assert "Band" in labels +def test_patch_linewidth_rc_controls_patch_edgefix() -> None: + """ + Patch-style artists should honor rc patch linewidth even when edge-fix is active. + """ + expected = 3.5 + with uplt.rc.context({"patch.linewidth": expected}): + fig, axs = uplt.subplots(ncols=4) + + bar = axs[0].bar([1, 2], [3, 4]) + fill = axs[1].fill_between([0, 1, 2], [1, 2, 1]) + hist = axs[2].hist(np.arange(5)) + pie = axs[3].pie([1, 2, 3]) + + assert [patch.get_linewidth() for patch in bar.patches] == pytest.approx( + [expected, expected] + ) + assert np.atleast_1d(fill.get_linewidths()) == pytest.approx([expected]) + assert [patch.get_linewidth() for patch in hist[2]] == pytest.approx( + [expected] * len(hist[2]) + ) + assert [wedge.get_linewidth() for wedge in pie[0]] == pytest.approx( + [expected] * len(pie[0]) + ) + + uplt.close(fig) + + +def test_patch_linewidth_rc_does_not_override_collection_edgefix() -> None: + """ + Collection-style 2D artists keep their dedicated edge-fix linewidth. + """ + with uplt.rc.context({"patch.linewidth": 3.5}): + fig, ax = uplt.subplots() + mesh = ax.pcolormesh(np.arange(9).reshape(3, 3)) + assert np.atleast_1d(mesh.get_linewidth()) == pytest.approx([0.3]) + + uplt.close(fig) + + def test_graph_nodes_kw(): """Test the graph method by setting keywords for nodes""" import networkx as nx