How to rotate 3D Plotly in R, update?

Viewed 438

I want to make an animated plot with a 3D surface

I was trying to replicate this example

https://stackoverflow.com/a/66117098/11555164

But I was unable to make it work, is not rotating

library(shiny)
library(plotly)
library(htmlwidgets)

ui <- fluidPage(
  plotlyOutput("graph")
)

server <- function(input, output, session) {
  N <- 100
  x <- rnorm(N, mean = 50, sd = 2.3)
  y <- runif(N, min = 0, max = 100)
  z <- runif(N, min = 4, max = 70)
  luci.frame <- data.frame(x, y, z)
  
  output$graph <- renderPlotly({
    plot_ly(
      type = "scatter3d",
      mode = "markers",
      data = luci.frame,
      x = ~ x,
      y = ~ y,
      z = ~ z
    ) %>%
      layout(scene = list(camera = list(
        eye = list(
          x = 1.25,
          y = 1.25,
          z = 1.25
        ),
        center = list(x = 0,
                      y = 0,
                      z = 0)
      ))) %>%
      onRender("
      function(el, x){
  var id = el.getAttribute('id');
  var gd = document.getElementById(id);
  Plotly.plot(id).then(attach);
  function attach() {
    var cnt = 0;
    
    function run() {
      rotate('scene', Math.PI / 180);
      requestAnimationFrame(run);
    } 
    run();
    
    function rotate(id, angle) {
      var eye0 = gd.layout[id].camera.eye
      var rtz = xyz2rtz(eye0);
      rtz.t += angle;
      
      var eye1 = rtz2xyz(rtz);
      Plotly.relayout(gd, id + '.camera.eye', eye1)
    }
    
    function xyz2rtz(xyz) {
      return {
        r: Math.sqrt(xyz.x * xyz.x + xyz.y * xyz.y),
        t: Math.atan2(xyz.y, xyz.x),
        z: xyz.z
      };
    }
    
    function rtz2xyz(rtz) {
      return {
        x: rtz.r * Math.cos(rtz.t),
        y: rtz.r * Math.sin(rtz.t),
        z: rtz.z
      };
    }
  };
}
    ")
  })
}

shinyApp(ui, server)

I don't get any error in the console of Rstudio

I also try to isolate the code, and avoid the use of Shiny (to save it as HTML) And in Chrome, I get and rendering error when opening the Debugging

Chrome error

This is the second code (without shiny)

#library(shiny)
library(plotly)
library(htmlwidgets)

N <- 100
x <- rnorm(N, mean = 50, sd = 2.3)
y <- runif(N, min = 0, max = 100)
z <- runif(N, min = 4, max = 70)
luci.frame <- data.frame(x, y, z)

plot_ly(
  type = "scatter3d",
  mode = "markers",
  data = luci.frame,
  x = ~ x,
  y = ~ y,
  z = ~ z
) %>%
  layout(scene = list(camera = list(
    eye = list(
      x = 1.25,
      y = 1.25,
      z = 1.25
    ),
    center = list(x = 0,
                  y = 0,
                  z = 0)
  ))) %>%
  onRender("
      function(el, x){
  var id = el.getAttribute('id');
  var gd = document.getElementById(id);
  Plotly.plot(id).then(attach);
  function attach() {
    var cnt = 0;
    
    function run() {
      rotate('scene', Math.PI / 180);
      requestAnimationFrame(run);
    } 
    run();
    
    function rotate(id, angle) {
      var eye0 = gd.layout[id].camera.eye
      var rtz = xyz2rtz(eye0);
      rtz.t += angle;
      
      var eye1 = rtz2xyz(rtz);
      Plotly.relayout(gd, id + '.camera.eye', eye1)
    }
    
    function xyz2rtz(xyz) {
      return {
        r: Math.sqrt(xyz.x * xyz.x + xyz.y * xyz.y),
        t: Math.atan2(xyz.y, xyz.x),
        z: xyz.z
      };
    }
    
    function rtz2xyz(rtz) {
      return {
        x: rtz.r * Math.cos(rtz.t),
        y: rtz.r * Math.sin(rtz.t),
        z: rtz.z
      };
    }
  };
}
    ")

Im missing something on onRender?


Update: Thanks to @ismirsehregal for the update in the change of Plotly.plot to Plotly.update You can check the accepted answer

In case you need the version without Shiny, you can use this code

library(plotly)
library(htmlwidgets)

N <- 100
x <- rnorm(N, mean = 50, sd = 2.3)
y <- runif(N, min = 0, max = 100)
z <- runif(N, min = 4, max = 70)
luci.frame <- data.frame(x, y, z)

PLT <- plot_ly(
  type = "scatter3d",
  mode = "markers",
  data = luci.frame,
  x = ~ x,
  y = ~ y,
  z = ~ z
) %>%
  layout(scene = list(camera = list(
    eye = list(
      x = 1.25,
      y = 1.25,
      z = 1.25
    ),
    center = list(x = 0,
                  y = 0,
                  z = 0)
  ))) %>%
  onRender("
      function(el, x){
  var id = el.getAttribute('id');
  var gd = document.getElementById(id);
  Plotly.update(id).then(attach);
  function attach() {
    var cnt = 0;
    
    function run() {
      rotate('scene', Math.PI / 1000);
      requestAnimationFrame(run);
    } 
    run();
    
    function rotate(id, angle) {
      var eye0 = gd.layout[id].camera.eye
      var rtz = xyz2rtz(eye0);
      rtz.t += angle;
      
      var eye1 = rtz2xyz(rtz);
      Plotly.relayout(gd, id + '.camera.eye', eye1)
    }
    
    function xyz2rtz(xyz) {
      return {
        r: Math.sqrt(xyz.x * xyz.x + xyz.y * xyz.y),
        t: Math.atan2(xyz.y, xyz.x),
        z: xyz.z
      };
    }
    
    function rtz2xyz(rtz) {
      return {
        x: rtz.r * Math.cos(rtz.t),
        y: rtz.r * Math.sin(rtz.t),
        z: rtz.z
      };
    }
  };
}
    ")

PLT
1 Answers

R plotly 4.10.0 recently updated the underlying plotly.js library from v1.57.1 to v2.5.1. This includes many breaking changes - With version 2.0 of plotly.js the function Plotly.plot was dropped.

To get back the old behaviour Plotly.plot can be replaced by Plotly.update:

library(shiny)
library(plotly)
library(htmlwidgets)

ui <- fluidPage(
  plotlyOutput("graph")
)

server <- function(input, output, session) {
  N <- 100
  x <- rnorm(N, mean = 50, sd = 2.3)
  y <- runif(N, min = 0, max = 100)
  z <- runif(N, min = 4, max = 70)
  luci.frame <- data.frame(x, y, z)
  
  output$graph <- renderPlotly({
    plot_ly(
      type = "scatter3d",
      mode = "markers",
      data = luci.frame,
      x = ~ x,
      y = ~ y,
      z = ~ z
    ) %>%
      layout(scene = list(camera = list(
        eye = list(
          x = 1.25,
          y = 1.25,
          z = 1.25
        ),
        center = list(x = 0,
                      y = 0,
                      z = 0)
      ))) %>%
      onRender("
      function(el, x){
  var id = el.getAttribute('id');
  var gd = document.getElementById(id);
  Plotly.update(id).then(attach);
  function attach() {
    var cnt = 0;
    
    function run() {
      rotate('scene', Math.PI / 180);
      requestAnimationFrame(run);
    } 
    run();
    
    function rotate(id, angle) {
      var eye0 = gd.layout[id].camera.eye
      var rtz = xyz2rtz(eye0);
      rtz.t += angle;
      
      var eye1 = rtz2xyz(rtz);
      Plotly.relayout(gd, id + '.camera.eye', eye1)
    }
    
    function xyz2rtz(xyz) {
      return {
        r: Math.sqrt(xyz.x * xyz.x + xyz.y * xyz.y),
        t: Math.atan2(xyz.y, xyz.x),
        z: xyz.z
      };
    }
    
    function rtz2xyz(rtz) {
      return {
        x: rtz.r * Math.cos(rtz.t),
        y: rtz.r * Math.sin(rtz.t),
        z: rtz.z
      };
    }
  };
}
    ")
  })
}

shinyApp(ui, server)
Related