From 9cf3f001d7d01331a2be19c7b161597d716adcb4 Mon Sep 17 00:00:00 2001 From: cvanelteren Date: Fri, 28 Aug 2026 06:33:15 +1000 Subject: [PATCH 1/2] fix an issue iwth z is forwarded --- ultraplot/axes/plot.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/ultraplot/axes/plot.py b/ultraplot/axes/plot.py index 55fcb78a5..7e35a79d0 100644 --- a/ultraplot/axes/plot.py +++ b/ultraplot/axes/plot.py @@ -7632,9 +7632,16 @@ def tripcolor(self, *args, **kwargs): # Update kwargs and handle cmap kw.update(_pop_props(kw, "collection")) + # NOTE: Matplotlib colors either the vertices, from the positional z, or + # the faces, from the 'facecolors' keyword. Since the input parser pads + # missing positional arguments with None, forwarding a null z would make + # matplotlib warn that the positional parameter has no effect. + values = z + if z is None: + values = kw.get("facecolors", None) center_levels = kw.pop("center_levels", None) kw = self._parse_cmap( - triangulation.x, triangulation.y, z, center_levels=center_levels, **kw + triangulation.x, triangulation.y, values, center_levels=center_levels, **kw ) # Handle patch edges, labels, and guide parameters @@ -7643,8 +7650,9 @@ def tripcolor(self, *args, **kwargs): guide_kw = _pop_params(kw, self._update_guide) # Plot with the native tripcolor method + zs = () if z is None else (z,) with self._keep_grid_bools(): - m = self._call_native("tripcolor", triangulation, z, **kw) + m = self._call_native("tripcolor", triangulation, *zs, **kw) # Fix edges and add labels self._fix_patch_edges(m, **edgefix_kw, **kw) From 2bafaddf6d210fc65c524b0f2e15d08a71290c50 Mon Sep 17 00:00:00 2001 From: cvanelteren Date: Fri, 28 Aug 2026 06:47:07 +1000 Subject: [PATCH 2/2] add smoke tests --- ultraplot/tests/test_2dplots.py | 57 +++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/ultraplot/tests/test_2dplots.py b/ultraplot/tests/test_2dplots.py index d0d971e9d..583ac8eeb 100644 --- a/ultraplot/tests/test_2dplots.py +++ b/ultraplot/tests/test_2dplots.py @@ -396,6 +396,63 @@ def test_tricontour_levels_respect_explicit_vmin_vmax(): assert m.norm(0.5) == pytest.approx(0.625) +def _triangulation(): + """ + Return a two-triangle unit square, for the tripcolor tests below. + """ + from matplotlib.tri import Triangulation + + return Triangulation( + [0.0, 1.0, 0.0, 1.0], + [0.0, 0.0, 1.0, 1.0], + [[0, 1, 2], [1, 3, 2]], + ) + + +def test_tripcolor_facecolors_without_z(): + """ + Coloring the faces needs no z, and must not warn about a positional c. + + The input parser pads absent positional arguments with None, and forwarding + that null z made matplotlib warn that the positional parameter had no + effect. See the tripcolor override in `ultraplot.axes.plot`. + """ + facecolors = np.array([1.0, 2.0]) + _, ax = uplt.subplots() + with warnings.catch_warnings(): + warnings.simplefilter("error", UserWarning) + m = ax.tripcolor(_triangulation(), facecolors=facecolors) + assert np.array_equal(m.get_array(), facecolors) + assert m.get_clim() == pytest.approx((1.0, 2.0)) + + +def test_tripcolor_vertex_values_unaffected(): + """ + Passing z still colors the vertices, and still sets the color limits. + """ + z = np.array([0.0, 1.0, 2.0, 3.0]) + _, ax = uplt.subplots() + with warnings.catch_warnings(): + warnings.simplefilter("error", UserWarning) + m = ax.tripcolor(_triangulation(), z) + vmin, vmax = m.get_clim() + assert vmin == pytest.approx(0.0) + assert vmax == pytest.approx(3.0, abs=1e-6) + + +def test_tripcolor_warns_when_z_and_facecolors_given(): + """ + Supplying both is a real conflict: matplotlib discards z, and says so. + """ + _, ax = uplt.subplots() + with pytest.warns(UserWarning, match="Positional parameter c has no effect"): + ax.tripcolor( + _triangulation(), + np.array([0.0, 1.0, 2.0, 3.0]), + facecolors=np.array([1.0, 2.0]), + ) + + def test_tricontour_explicit_colors_match_levels(): """ Explicit triangular contour colors should map one-to-one with levels.