How to Rotate 3D Plotly Continuous for R Shiny App

How to rotate 3D Plotly continuous for R shiny App

We can reuse most of the JS code via htmlwidgets::onRender. You tagged the question shiny - wrapped it in an app accordingly:

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)

result1


The same can be done via plotlyProxy without additional JS - but it's not as smooth:

library(shiny)
library(plotly)

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)

mySequence <- seq(0, 100, by = 0.1)

cam.zoom = 2
# ver.angle = 0

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 = cos(mySequence[1]) * cam.zoom,
y = sin(mySequence[1]) * cam.zoom,
z = 0.3
),
center = list(x = 0,
y = 0,
z = 0)
)))
})

myPlotlyProxy <- plotlyProxy("graph")
count <- reactiveVal(1L)

observe({
invalidateLater(100)
plotlyProxyInvoke(myPlotlyProxy, "relayout", list(scene = list(camera = list(
eye = list(
x = cos(mySequence[isolate(count())]) * cam.zoom,
y = sin(mySequence[isolate(count())]) * cam.zoom,
z = 0.3
),
center = list(x = 0,
y = 0,
z = 0)
))))

isolate(count(count()+1))

if(count() > length(mySequence)){
count(1L)
}
})
}

shinyApp(ui, server)

result2

How to rotate 3D Plotly in R, update?

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)

Plot_ly rotate animation in R

This is taken from my earlier answer here.

We can use the code without shiny and save a standalone HTML file via htmlwidgets::saveWidget:

library(plotly)
library(htmlwidgets)
library(utils)

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)

fig <- 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
};
}
};
}
")

htmlwidgets::saveWidget(partial_bundle(fig), file = "rotate_scatter3d.HTML", selfcontained = TRUE)

utils::browseURL("rotate_scatter3d.HTML")

result

Spinning 3D Scatterplots inside a Shiny app

Ok, I just got it. Thanks to Joe Cheng, I decided to use threejs and work neatly!

Now, I have at UI.R

uiOutput("ScatterPlot")

And at Server.R

  output$plott <- renderScatterplotThree({

col <- c(rep("#7A99AC",table(xyvarData()[,'variable'])[[1]]),rep("#E4002B",table(xyvarData()[,'variable'])[[2]]))
scatterplot3js(xyvarData()[,1],xyvarData()[,2],xyvarData()[,'value'], color=col, size=0.5,
axisLabels=c(input$featureDisplay_x,"prop.conversions",input$featureDisplay_y),zlim=c(0,1))

})
output$ScatterPlot <- renderUI({
scatterplotThreeOutput("plott")
})

Instead of webGLOutput() and renderWebGL({}).

Test if an object is plotly

class(x)

returns

[1] "plotly"     "htmlwidget"

where x is created using ggplotly.

So how about something like:

"plotly" %in% class(x)
[1] TRUE

You could make it into a function is.plotly:

is.plotly <- function(x) {
"plotly" %in% class(x)
}


Related Topics



Leave a reply



Submit