From 113027624c0f49db737844729f2de273b48326f5 Mon Sep 17 00:00:00 2001 From: Roberto Moura Date: Mon, 10 Aug 2026 19:17:55 +0000 Subject: [PATCH 1/2] Clamp bargap to [0, 1] in get_bar_gap --- plotly/matplotlylib/mpltools.py | 4 +++- plotly/matplotlylib/tests/test_renderer.py | 14 ++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/plotly/matplotlylib/mpltools.py b/plotly/matplotlylib/mpltools.py index 0a3206998b..06732cb946 100644 --- a/plotly/matplotlylib/mpltools.py +++ b/plotly/matplotlylib/mpltools.py @@ -269,7 +269,9 @@ def get_bar_gap(bar_starts, bar_ends, tol=1e-10): gap0 = gaps[0] uniform = all([abs(gap0 - gap) < tol for gap in gaps]) if uniform: - return gap0 + # plotly's bargap must be in [0, 1]; clamp to guard against + # floating point noise (e.g. -8.9e-16 for touching bars) + return min(max(gap0, 0.0), 1.0) def convert_rgba_array(color_list): diff --git a/plotly/matplotlylib/tests/test_renderer.py b/plotly/matplotlylib/tests/test_renderer.py index f56d830917..b5ade651f1 100644 --- a/plotly/matplotlylib/tests/test_renderer.py +++ b/plotly/matplotlylib/tests/test_renderer.py @@ -199,3 +199,17 @@ def test_filled_path_collection_date_xaxis(): filled = [t for t in plotly_fig.data if t.fill == "toself"] assert len(filled) >= 1 assert all(isinstance(x, str) for x in filled[0].x) + + +def test_get_bar_gap_clamps_negative_float_noise(): + """Touching bars can produce a tiny negative gap from floating point + noise (e.g. -8.88e-16 for a histogram); plotly rejects bargap outside + [0, 1], so the gap must be clamped.""" + from plotly.matplotlylib.mpltools import get_bar_gap + + # touching bars: gap is exactly 0 + assert get_bar_gap([0.0, 1.0], [1.0, 2.0]) == 0.0 + # overlapping-by-noise bars: gap is a tiny negative float, clamped to 0 + assert get_bar_gap([0.0, 1.0], [1.0 + 1e-15, 2.0]) == 0.0 + # positive gaps are unchanged + assert get_bar_gap([0.0, 2.0], [1.0, 3.0]) == 1.0 From 8437938f5650d86922e69a61270ac5ad48fa3caa Mon Sep 17 00:00:00 2001 From: Roberto Moura Date: Fri, 21 Aug 2026 12:09:57 +0000 Subject: [PATCH 2/2] Add regression test for histogram conversion with clamped bargap --- plotly/matplotlylib/tests/test_renderer.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/plotly/matplotlylib/tests/test_renderer.py b/plotly/matplotlylib/tests/test_renderer.py index b5ade651f1..663366e531 100644 --- a/plotly/matplotlylib/tests/test_renderer.py +++ b/plotly/matplotlylib/tests/test_renderer.py @@ -213,3 +213,16 @@ def test_get_bar_gap_clamps_negative_float_noise(): assert get_bar_gap([0.0, 1.0], [1.0 + 1e-15, 2.0]) == 0.0 # positive gaps are unchanged assert get_bar_gap([0.0, 2.0], [1.0, 3.0]) == 1.0 + + +def test_histogram_converts(): + """Histograms must convert without error and keep bargap in plotly's + valid [0, 1] range; get_bar_gap can return a gap with floating point + noise for touching bars, which plotly rejects.""" + fig, ax = plt.subplots() + ax.hist(np.random.randn(1000), 30) + + plotly_fig = tls.mpl_to_plotly(fig) + + assert len(plotly_fig.data) == 1 + assert 0 <= plotly_fig.layout.bargap <= 1