Skip to content

[BUG]: fillgradient with start/stop ignores zoom and fails on secondary, log and radial cases #8095

Description

@dewi-ny-je

Description

Found while implementing #8093

A scatter fillgradient with start or stop has four problems in plotly.js 4.1.0. All four come from the user-space branch of setFillStyle in src/components/drawing/index.js.

  • The gradient does not follow a zoom or a pan. gradientWithBounds sets x1/y1/x2/y2 only when it creates the . After a relayout of the axis range, the data key (type and color stops) is unchanged. So the element keeps its old pixel bounds, and the colors no longer match the axis values.
  • A single start or stop throws on a secondary axis. The missing bound reads trace._extremes.x or trace._extremes.y. But _extremes is keyed by axis id, so a trace on y2 throws TypeError: Cannot read properties of undefined (reading 'max'), and the plot does not draw.
  • A single start or stop gives a wrong bound on a log axis. The extremes are in linear (l) space, but the code converts them with c2p. On a log axis, the missing bound lands at log10(max) instead of max.
  • A radial gradient with start or stop throws. The attribute descriptions say that start and stop are ignored for radial. But the code enters the user-space branch, start stays undefined, and the plot throws TypeError: Cannot read properties of undefined (reading 'x').

Screenshots/Video

Case 1, before and after Plotly.relayout(gd, {'yaxis.range': [0, 10]}). The fill must be red above y = 2 in both images:

Image Image

All four cases on 4.1.0, from the test case below. Case 2 and case 4 draw a black fill because the style step throws:

Image

Steps to reproduce

Copy the following code in JSFiddle or JSbin, or save it as HTML and open it in Chrome:

<script src="https://cdn.plot.ly/plotly-4.1.0.js"></script>
<div id="zoom" style="width:500px;height:350px"></div>
<div id="y2" style="width:500px;height:350px"></div>
<div id="log" style="width:500px;height:350px"></div>
<div id="radial" style="width:500px;height:350px"></div>
<script>
const cs = [[0, 'blue'], [0.5, 'blue'], [0.5, 'red'], [1, 'red']];

// 1. Expected: red above y = 2 after the relayout. Actual: no red.
Plotly.newPlot('zoom', [{
  x: [0, 1, 2], y: [0, 4, 0], fill: 'tozeroy',
  fillgradient: { type: 'vertical', start: 0, stop: 4, colorscale: cs }
}], { yaxis: { range: [0, 5] } })
  .then(gd => Plotly.relayout(gd, { 'yaxis.range': [0, 10] }));

// 2. Expected: red above y2 = 2. Actual: TypeError (reading 'max').
Plotly.newPlot('y2', [
  { y: [1, 2] },
  { y: [1, 3, 2], yaxis: 'y2', fill: 'tozeroy',
    fillgradient: { type: 'vertical', start: 1, colorscale: cs } }
], { yaxis2: { overlaying: 'y', side: 'right' } }).catch(console.error);

// 3. Expected: red above y = 10, half way between 1 and 100 on the log axis. Actual: almost all red.
Plotly.newPlot('log', [{
  y: [1, 100, 10], fill: 'toself',
  fillgradient: { type: 'vertical', start: 1, colorscale: cs }
}], { yaxis: { type: 'log' } });

// 4. Expected: a radial gradient. Actual: TypeError (reading 'x').
Plotly.newPlot('radial', [{
  y: [1, 3, 2], fill: 'tozeroy',
  fillgradient: { type: 'radial', start: 1, colorscale: cs }
}]).catch(console.error);
</script>
  • Open the page and wait one second
  • Note that plot 1 has no red fill, although the fill reaches y = 4
  • Note the black fills in plots 2 and 4
  • Note that plot 3 is red almost down to y = 1, instead of only above y = 10

Notes

  • Case 1 also affects drag zoom, pan and scroll zoom. Any range change runs drawData, but gradientWithBounds does not update the existing gradient element.
  • A fix is ready on a fork branch, bugfix/fillgradient-bugs. It moves the bound computation into one function that reads trace._extremes[ax._id], converts the extremes with l2p, and skips the user-space branch for radial.
  • gradientWithBounds then sets the bounds on every call. The branch adds four jasmine tests. Each test fails on main and passes with the fix. No existing baseline moves.
  • Fix available, will be provided in PR

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions