Skip to content

3D plot redraw() inside a listener cause a recursive loop #1025

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
FMiscia opened this issue Oct 11, 2016 · 15 comments
Open

3D plot redraw() inside a listener cause a recursive loop #1025

FMiscia opened this issue Oct 11, 2016 · 15 comments
Labels
bug something broken P2 considered for next cycle

Comments

@FMiscia
Copy link

FMiscia commented Oct 11, 2016

I have the following code to be executed:

this.$.plot3d.on('plotly_click', function(d){
  self.data[1].x.push(parseFloat(d.points[0].x))
  self.data[1].y.push(parseFloat(d.points[0].y))
  self.data[1].z.push(parseFloat(d.points[0].z))
  redrawing = true
  Plotly.redraw(self.$.plot3d); 
})

It just adds a 3D point (x,y,z) to a trace and then it redraws the plot.
If I do like this, as far as I seen, the redraw method somewhere trigger again the plotly_click event and this cause a recursive loop.

I used an ugly workaround to fix this, which is the following:

this.$.plot3d.on('plotly_click', function(d){
  if(!redrawing){        
    self.data[1].x.push(parseFloat(d.points[0].x))
    self.data[1].y.push(parseFloat(d.points[0].y))
    self.data[1].z.push(parseFloat(d.points[0].z))
    redrawing = true
    Plotly.redraw(self.$.plot3d);
  } else {
    redrawing = false
  }
})

But it's not hte best way. Am I do correctly redrawing the graph in a listener? Is there any other way (maybe the right one) to redraw a graph after a point selection? Is this allowed?

Thank you

@etpinard
Copy link
Contributor

Am I do correctly redrawing the graph in a listener?

Again here, providing a fully-reproducible example would help us help you.

@FMiscia
Copy link
Author

FMiscia commented Oct 11, 2016

Sorry again, here you go the example. Clicking on a point, the browser page basically crash because of a recursive loop.

@etpinard etpinard added the bug something broken label Oct 13, 2016
@etpinard
Copy link
Contributor

Thanks for the report.

@kelegorm
Copy link

kelegorm commented Mar 3, 2017

This bug is reproducible since version 1.10.0.

@kelegorm
Copy link

kelegorm commented Mar 6, 2017

Ok, how I see problem. in 1.6.0 version was presented fix for issue #240. It added click events for 3D scattergl. But it was done in strange way: clicks are firing on each redrawing while mouse button is down. For example, in same case 2D event is fired once mouse is up. But this is only part of issue.

Somewhere between 1.9.0 and 1.10.0 some change was done that makes things go recursively: now, if mouse was down and in it's callback was redrawing event fires and fires again even after mouse was released, so selection.buttons == true all the time.

May be it was force redrawing, may be - some other change.

I hope, that info will help somebody to fix that problem, I still can't get root of problem, what need to change. And while we are using both 2d and 3d scenes it's hard to make easy way to overcome that problem.

@etpinard
Copy link
Contributor

etpinard commented Mar 6, 2017

Thanks for your investigation work @kelegorm 🔬

@svkucheryavski
Copy link

May I ask if anyone found a workaround for the problem? Thanks!

@spiffomatic64
Copy link

I got around this using a setTimeout of 100ms in the click handler

@svkucheryavski
Copy link

Thank you!

@Florent-Breton
Copy link

Florent-Breton commented Nov 19, 2019

Hi,
Is there a good resolution of this bug? I have also this problem, even with just 1 point. I tried a setTimeout of 100ms but without any result.

`myPlot.on('plotly_click', function (data) {
        console.log("data :", data)
        var pn = data.points[0].pointNumber,
            tn = data.points[0].curveNumber,
            colors = data.points[0].data.marker.color;

        colors[pn] = '#C54C82';

        var update = { 'marker': { color: colors, size: 4 } };
        Plotly.restyle('chart', update, [tn]);
    });
`

plotly-latest.min.js:7 Uncaught RangeError: Maximum call stack size exceeded
at e.exports (plotly-latest.min.js:7)
at e.exports (plotly-latest.min.js:7)
at e.exports (plotly-latest.min.js:7)
at o.merge (plotly-latest.min.js:7)
at b._.plot (plotly-latest.min.js:7)
at Object.r.plot (plotly-latest.min.js:7)
at r.drawData (plotly-latest.min.js:7)
at Object.l.syncOrAsync (plotly-latest.min.js:7)
at r.plot (plotly-latest.min.js:7)
at Object.l.syncOrAsync (plotly-latest.min.js:7)

@etpinard
Copy link
Contributor

etpinard commented Jan 28, 2020

Additional report from @Luthaf in #4513


See https://codepen.io/luthaf/pen/mdyvGLR for a reproduction. Clicking on one of the points fire the event in a loop and the script ends up killed by the browser (too much recursion on firefox).

I find it strange that calling Plotly.restyle would send a plotly_click event, hence this issue!

@mikemey
Copy link

mikemey commented Aug 7, 2020

I've found an awful workaround for this issue: place the call to Plotly.redraw/Plotly.restyle in a timeout with a small delay:

setTimeout(() => Plotly.restyle( ... ), 200)

Sometimes I still get multiple calls, but it stops the infinite event loop.

I'll take a shower, I feel dirty...

@jxu
Copy link

jxu commented Jun 23, 2021

Still experiencing it with 2.1.0

@jxu
Copy link

jxu commented Jun 23, 2021

Another workaround is to just use newPlot instead of redraw, but this obviously redraws the whole plot and requires rebinding the click event

@bolau
Copy link

bolau commented Feb 28, 2024

This still is still a problem in 2024. Here's my workaround:

Use a boolean handlersActive to determine, if the click and selection handlers should do anything.
When redrawing, set the boolean to false, and back to active with a timeout.

Below is an example. Although incomplete excerpts copied from my code, you'll get the idea.

These are my handlers in my vue-plotly wrapper:

document.getElementById(this.plotlyId).on('plotly_click', (data) => {
    if (this.handlersActive) this.$emit("pointClick", data)
})
document.getElementById(this.plotlyId).on('plotly_selected', (data) => {
    if (this.handlersActive) this.$emit("plotlySelected", data)
})

And here's the update function:

this.handlersActive = false;
Plotly.react(this.plotlyId, this.data, this.layout, this.config);
setTimeout( () => {
    this.handlersActive = true
}, 10)

Hope that helps
Best, Boris

@gvwilson gvwilson self-assigned this Jun 5, 2024
@gvwilson gvwilson removed their assignment Aug 2, 2024
@gvwilson gvwilson added P2 considered for next cycle and removed ♥ NEEDS SPON$OR labels Aug 2, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug something broken P2 considered for next cycle
Projects
None yet
Development

No branches or pull requests