Skip to content
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

How can I preserve the view state of the plot? #90

Closed
neilyoung opened this issue Jul 13, 2018 · 20 comments
Closed

How can I preserve the view state of the plot? #90

neilyoung opened this issue Jul 13, 2018 · 20 comments

Comments

@neilyoung
Copy link

I'm providing new x, y and z values on a time based manner to the plot. As expected the plot is newly rendered, since the values are pushed through the state Now I would like to preserve the previous zoom, pan, tilt and apply it at render time, so that e.g. an outer rotation is not reverted by the redraw. How can I achieve this?

@nicolaskruchten
Copy link
Contributor

You'll need to implement the pattern from here: https://github.com/plotly/react-plotly.js#state-management

@neilyoung
Copy link
Author

Tried that, but

onUpdate={(figure) => this.setState(figure)}

setState(figure) causes and infinite loop

@neilyoung
Copy link
Author

OK, maybe the infinite loop was my bad... But whatsoever I do with my data arrays, I can't make them draw the plot with new data. I tried to manipulate datarevision in layout, this seems to work: A new plot is drawn on every data update, but each pose is lost (rotation, zoom, tilt, pan). I also tried top-level property revision but altering this seems to make no difference at all.

Clueless...

@neilyoung
Copy link
Author

Beat me, but this component behaves weird. It updates and keeps the zoom now. But rotation and pan are lost. Giving up for now :(

@neilyoung
Copy link
Author

neilyoung commented Jul 13, 2018

Best: If the pan/rotation happens between two data updates, the rotation is kept... This, BTW, also explains, why I had the impression, zoom would be kept. You simply do not notice (if you don't try) that the zoom is reset in between a zoom in / out...

I need to prevent data updates while manipulating the position of the plot. Ideas?

@nicolaskruchten
Copy link
Contributor

If you could provide a sample of what you're trying to do, e.g. in a CodePen or something, it would be easier for me to try to help you, as I'm having trouble understanding what's happening here for you :)

@neilyoung
Copy link
Author

Thanks for the offer. I will consider that. I simply need to draw a timely changing series of x, y and z values. This works somehow. Think about drawing the track of a plane or pedestrian in time (maybe for performance reasons just a little span of time). As said, this works somehow. Unfortunately the camera view is with every data update reset to something shown in the screenshot.

localhost_3000

So it contradicts the intention of the user, who might want to view the scene from different angles. If my data update interval is large enough to complete a camera scene change, then everything is OK and the next data update doesn't change it back to the initial view. Unfortunately this is not possible to achieve with data update intervals in millisecond range.

It would be great if the component would not accept redraws forced by data update while in a rotation or pan or zoom. I'm sure this is a "defect" of the original library and has nothing to do with the react wrap

@neilyoung
Copy link
Author

OK, here is my sample code, which works somehow. If I change the rotation in between the two seconds of update, everything is fine, but please set the interval to 250 and try again...

import React, { Component } from 'react'
import { hot } from 'react-hot-loader'

let Plot
if (typeof document !== 'undefined') {
  Plot = require('react-plotly.js')
}

class App extends Component {
  constructor(props) {
    super(props)

    this.x = [1, 2, 3]
    this.y = [2, 3, 1]
    this.z = [5, 7, 8]

    this.tick = this.tick.bind(this)
    this.state = {
      data: [{
        x: this.x,
        y: this.y,
        z: this.z,
        type: 'scatter3d',
        mode: 'bars',
        marker: { color: 'red' },
      }],
      layout: {
        width: 1024,
        height: 768,
        autoSize: true,
        staticPlot: true,
        title: 'Plot',
      },
      frames: [],
      config: {}
    }
  }

  componentDidMount() {
    this.timer = setInterval(this.tick, 2000)
  }

  componentWillUnmount() {
    clearInterval(this.tick)
  }

  tick() {

    let data = this.state.data
    let x = data[0].x.slice()
    let y = data[0].y.slice()
    let z = data[0].z.slice()

    x.push(Math.floor((Math.random() * 100) + 1))
    y.push(Math.floor((Math.random() * 100) + 1))
    z.push(Math.floor((Math.random() * 100) + 1))

    data[0].x = x
    data[0].y = y
    data[0].z = z


    this.setState({
      config: {}
    })
  }

  render() {
    return (
      <div className="App">

        {typeof document !== 'undefined' &&
          <Plot
            data={this.state.data}
            layout={this.state.layout}
            frames={this.state.frames}
            config={this.state.config}
            onInitialized={(figure) => this.setState(figure)}
            onUpdate={(figure) => { this.setState(figure) }}
          />
        }
      </div>
    )
  }
}

export default hot(module)(App)

@nicolaskruchten
Copy link
Contributor

Ah I understand the problem... That's an interesting one, I'll have to chat with the plotly.js team to see what they think/if this works outside of the Plotly.react() context. Thanks for sharing!

@neilyoung
Copy link
Author

Thanks for dealing with this. Have a nice weekend.

@etpinard
Copy link
Contributor

So yeah 3D doesn't behave the same way as cartesian subplots when dragging+updating data at once.

See comparison in:

where 3D drag update layout.scene.camera only on mouseup which results in a "camera block" in short interval loops. In cartesian, we currently attempt to prevent competing redraws calls during drag.

Unfortunately, the cartesian implementation is full of bugs too for example: plotly/plotly.js#2643 and plotly/plotly.js#2644

I think the easiest way to fix this problem (before plotly.js v2) would be to make a push for plotly/plotly.js#2606 and listen to the upcoming plotly_relayouting event to update the state properly.

@neilyoung
Copy link
Author

@etpinard @nicolaskruchten No issue. Thanks for your efforts. I have left plotly now in favor of visjs 3d. Works for me.

@Goran216
Copy link

I have the same problem. And I gave up this framework and had to use visjs. So sad and frustrating.

@nicolaskruchten
Copy link
Contributor

We have a new plotly.js feature coming soon for maintaining UI state!

@nicolaskruchten
Copy link
Contributor

plotly/plotly.js#3236

@neilyoung
Copy link
Author

@Goran216 I worked with visjs for a while and I was able to solve that problem. In the end it turned out, that visjs has no good performance on mobile devices, running in Webviews there.

So meanwhile I'm using Three.js, which is absolutely awesome. I'm able to have an animated 3d plot of more than 18.000 coordinates on an Android or iOS device with no visible loss in performance. That wasn't the end of lane, I stopped testing :)

@JamesRamm
Copy link

JamesRamm commented Jun 28, 2019

Tried that, but

onUpdate={(figure) => this.setState(figure)}

setState(figure) causes and infinite loop

Sorry for jumping in on this.
I have the same problem with setState causing an infinite loop - what was the problem you had?

I have a render method like this:

   render() {
      const { item, title, revision } = this.props;
      console.log("Render");

      if (item.loading) {
         return <FlexRow><Spinner large /></FlexRow>
      }

      return (
         <Plot
            revision={revision}
            data={item.data(false)}
            layout={this.state.layout}
            config={this.state.config}
            style={{ height: '90%' }}
            useResizeHandler
            onUpdate={figure => this.setState(figure)}
            onInitialized={figure => this.setState(figure)}
         />
      );
   }

Where the data is passed in as a prop ('item' is a actually a mobx model). But the onUpdate handler causes an infinite loop.

@elv1s42
Copy link

elv1s42 commented Oct 8, 2020

Hello everyone!
Looks like I'm facing the same issue.
Any updates, please?

@neilyoung
Copy link
Author

I think I somewhen in between gave up on this component and used three.js. Worked for me.

@elv1s42
Copy link

elv1s42 commented Oct 8, 2020

@nicolaskruchten, the issue is closed, but still, it exists and is reproducible

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants