Description
When a contour trace is drawn with xaxis.rangeslider.visible: true, the main plot is clipped to a strip the height of the range slider. The rest of the subplot is blank. The range slider thumbnail itself renders correctly.
This happens with the default connectgaps: false, even when z has no missing values. Heatmaps are not affected.
Reproduced in 4.1.1 (latest), 4.0.0, 3.7.0, 3.0.0, 2.35.3 and 1.58.5 (Chrome, headless and desktop).
Cause. clipGaps in src/traces/contour/plot.js creates a single <clipPath> per trace, keyed only by trace.uid, and computes its path with the axes of the subplot currently being drawn:
function clipGaps(plotGroup, plotinfo, gd, cd0, perimeter) {
var trace = cd0.trace;
var clips = gd._fullLayout._clips;
var clipId = 'clip' + trace.uid;
// ... path computed with plotinfo.xaxis / plotinfo.yaxis
Drawing.setClipUrl(plotGroup, clipId, gd);
}
The range slider draws the same trace again through rangePlot with its own small axes (isRangePlot: true). Both drawings share the same uid, so they share the same clipPath. The range plot is drawn last, so it overwrites the path with range-slider coordinates. The main plot then gets clipped by a shape that is only as tall as the slider.
In our measurements, the clip path bbox height equals the slider height (41px) instead of the plot area height (191px). Without the range slider, the clip path matches the plot area.
Screenshots/Video
Left: 4.1.1. Right: 4.1.1 with the one-line change below. Top row: full z. Bottom row: z with null cells.
Steps to reproduce
<script src="https://cdn.plot.ly/plotly-4.1.1.js"></script>
<div id="graph" style="width:480px;height:420px"></div>
<script>
var x = [0, 1, 2, 3, 4, 5];
var y = [0, 1, 2, 3, 4];
var z = y.map(function (j) { return x.map(function (i) { return 10 * (i + j); }); });
Plotly.newPlot('graph', [{ type: 'contour', x: x, y: y, z: z }], {
margin: { b: 90 },
xaxis: { rangeslider: { visible: true } }
});
</script>
- Open the page.
- Note that only a strip at the top of the main plot is drawn; the rest is blank.
- The same happens after
Plotly.restyle of x/y/z, after changing xaxis.range through the slider, and after relayout of a coloraxis.
- With
z containing nulls (e.g. [[10,20,30,40],[20,30,null,50],[30,null,50,60],[40,50,60,70]]), the main plot is clipped the same way. The gaps only show up correctly in the range-slider thumbnail.
Notes
Workarounds and why they fall short
connectgaps: true avoids the bug, because no clip path is created. But it changes the data: null cells get filled by interpolation, so the plot shows values that were never measured. That isn't acceptable when missing data must stay visible.
- Disabling the range slider for contour traces is what we do for now.
Proposed fix (validated locally)
Give the range-plot drawing its own clip id:
function clipGaps(plotGroup, plotinfo, gd, cd0, perimeter) {
var trace = cd0.trace;
var clips = gd._fullLayout._clips;
- var clipId = 'clip' + trace.uid;
+ var clipId = 'clip' + trace.uid + (plotinfo.isRangePlot ? '-rangeplot' : '');
I applied the equivalent change to the 4.1.1 dist bundle and re-ran the cases above:
- Full
z, z with nulls, restyle of x/y/z, range change from the slider, coloraxis relayout, and a grid filled row by row (25% → 100%) all render correctly.
- Gaps stay gaps in both the main plot and the thumbnail.
- The main clip path matches the plot area height again.
The change above is a suggestion based on local testing; I haven't opened a PR. Feel free to use it if it fits.
Possibly related: #705 (2016, contour + range slider, "some <clipPath> node is not getting updated properly"). It was closed as a duplicate of #558 for lack of a repro.
Description
When a
contourtrace is drawn withxaxis.rangeslider.visible: true, the main plot is clipped to a strip the height of the range slider. The rest of the subplot is blank. The range slider thumbnail itself renders correctly.This happens with the default
connectgaps: false, even whenzhas no missing values. Heatmaps are not affected.Reproduced in 4.1.1 (latest), 4.0.0, 3.7.0, 3.0.0, 2.35.3 and 1.58.5 (Chrome, headless and desktop).
Cause.
clipGapsinsrc/traces/contour/plot.jscreates a single<clipPath>per trace, keyed only bytrace.uid, and computes its path with the axes of the subplot currently being drawn:The range slider draws the same trace again through
rangePlotwith its own small axes (isRangePlot: true). Both drawings share the same uid, so they share the sameclipPath. The range plot is drawn last, so it overwrites the path with range-slider coordinates. The main plot then gets clipped by a shape that is only as tall as the slider.In our measurements, the clip path bbox height equals the slider height (41px) instead of the plot area height (191px). Without the range slider, the clip path matches the plot area.
Screenshots/Video
Left: 4.1.1. Right: 4.1.1 with the one-line change below. Top row: full
z. Bottom row:zwithnullcells.Steps to reproduce
Plotly.restyleofx/y/z, after changingxaxis.rangethrough the slider, and afterrelayoutof acoloraxis.zcontainingnulls (e.g.[[10,20,30,40],[20,30,null,50],[30,null,50,60],[40,50,60,70]]), the main plot is clipped the same way. The gaps only show up correctly in the range-slider thumbnail.Notes
Workarounds and why they fall short
connectgaps: trueavoids the bug, because no clip path is created. But it changes the data:nullcells get filled by interpolation, so the plot shows values that were never measured. That isn't acceptable when missing data must stay visible.Proposed fix (validated locally)
Give the range-plot drawing its own clip id:
function clipGaps(plotGroup, plotinfo, gd, cd0, perimeter) { var trace = cd0.trace; var clips = gd._fullLayout._clips; - var clipId = 'clip' + trace.uid; + var clipId = 'clip' + trace.uid + (plotinfo.isRangePlot ? '-rangeplot' : '');I applied the equivalent change to the 4.1.1 dist bundle and re-ran the cases above:
z,zwith nulls, restyle of x/y/z, range change from the slider,coloraxisrelayout, and a grid filled row by row (25% → 100%) all render correctly.The change above is a suggestion based on local testing; I haven't opened a PR. Feel free to use it if it fits.
Possibly related: #705 (2016, contour + range slider, "some
<clipPath>node is not getting updated properly"). It was closed as a duplicate of #558 for lack of a repro.