widgets sunburst networkd3 examples r shiny rgl

sunburst - shiny widgets



aplicación brillante rglwidget obtener userMatrix para generar otra gráfica con la misma rotación (1)

Tengo una aplicación brillante e integro un rgl 3d-plot en ella. Estoy usando renderRglwidget del paquete rglwidget para insertar los gráficos rgl usando webgl en mi brillante aplicación.

En la aplicación, el usuario puede rotar el gráfico. Ahora quiero guardar el estado de rotación, de ahí el userMatrix o modelMatrix para luego generar un diagrama similar con la misma rotación que el usuario dejó el gráfico anterior.

Aquí leo algo sobre las variables de Java que almacenan el userMatrix y otros parámetros. ¿Puedo acceder a ellos desde mi brillante aplicación (en código R)?

En rgl mismo, puedo usar rotationMatrix <- rgl.projection()$model o rotationMatrix <- par3d()$modelMatrix para almacenar la rotación del modelo. Pero como en mi caso el gráfico no gira en sí mismo, estas funciones no me ayudan.

Lo que probé es esto:

library(shiny) library(rgl) library(rglwidget) ui <- fluidPage( rglwidgetOutput("3D-plot"), actionButton("showMatrix", "show rotation Matrix") ) server <- function(input, output, session){ open3d(useNULL = T) x <- sort(rnorm(1000)) y <- rnorm(1000) z <- rnorm(1000) + atan2(x, y) plot3d(x, y, z, col = rainbow(1000)) scene1 <- scene3d() rgl.close() output$"3D-plot" <- renderRglwidget({ rglwidget(scene1) }) observe({ input$showMatrix par3d()$modelMatrix }) } shinyApp(ui=ui, server=server)

Pero par3d()$modelMatrix no parece devolver nada.


La razón por la que rgl:par3d() no devuelve nada es porque los paquetes rgl no están realmente administrando la escena para brillar. La biblioteca rglwidget basada en rglwidget que aprovecha WebGL está administrando, y está copiando sobre la escena a otra biblioteca GL muy compatible (tal vez incluso usan la misma biblioteca compilada, pero lo dudo) y mostrando eso en brillante. Entonces rgl.dev() no te ayudará.

AFAIK, no es fácil obtener estos valores, ya que están ocultos en rglwidget javascript, pero quería llegar a ellos, así que construí un control de entrada personalizado brillante que puede hacerlo. Fue una buena cantidad de trabajo, y podría haber una manera más fácil, pero no lo vi y al menos sé cómo crear controles de entrada personalizados para brillantes ahora. Si alguien conoce un camino más fácil, por favor ilumíname.

Aquí está el javascript, entra en una subcarpeta www que guarda en el mismo directorio que su código brillante.

rglwidgetaux.js

// rglwidgetaux control for querying shiny rglwiget var rglwidgetauxBinding = new Shiny.InputBinding(); $.extend(rglwidgetauxBinding, { find: function(scope) { return $(scope).find(".rglWidgetAux"); }, getValue: function(el) { return el.value; }, setValue: function(el, value) { // $(el).text(value); el.value = value; }, getState: function(el) { return { value: this.getValue(el) }; }, receiveMessage: function(el, data) { var $el = $(el); switch (data.cmd) { case "test":alert("Recieved Message"); break; case "getpar3d": var rglel = $("#"+data.rglwidgetId); if (rglel.length===0){ alert("bad rglwidgetId:"+ data.rglwidgetId); return null; } var rglinst = rglel[0].rglinstance; var sid = rglinst.scene.rootSubscene; var par3d = rglinst.getObj(sid).par3d; this.setValue(el,JSON.stringify(par3d)); $el.trigger("change"); // tell myself that I have changed break; } }, subscribe: function(el, callback) { $(el).on("change.rglwidgetauxBinding", function(e) { callback(); }); }, unsubscribe: function(el) { $(el).off(".rglwidgetauxBinding"); } }); Shiny.inputBindings.register(rglwidgetauxBinding);

Aquí está el código R / brillante. Utiliza la escena de prueba habitual y tiene un botón para consultar las escenas userMatrix y lo muestra en una tabla. userMatrix el userMatrix y no el modelMatrix porque el primero es fácil de cambiar con el mouse, por lo que puede ver que está obteniendo los valores más frescos.

Tenga en cuenta que el nombre "app.R" no es realmente opcional. O tiene que usar eso, o dividir el archivo en "ui.R" y "server.R", de lo contrario no importará el archivo javascript anterior.

app.R

library(shiny) library(rgl) library(htmlwidgets) library(jsonlite) rglwgtctrl <- function(inputId, value="", nrows, ncols) { # This code includes the javascript that we need and defines the html tagList( singleton(tags$head(tags$script(src = "rglwidgetaux.js"))), tags$div(id = inputId,class = "rglWidgetAux",as.character(value)) ) } ui <- fluidPage( rglwgtctrl(''ctrlplot3d''), actionButton("regen", "Regen Scene"), actionButton("queryumat", "Query User Matrix"), rglwidgetOutput("plot3d"), tableOutput("usermatrix") ) server <- function(input, output, session) { observe({ # tell our rglWidgetAux to query the plot3d for its par3d input$queryumat session$sendInputMessage("ctrlplot3d",list("cmd"="getpar3d","rglwidgetId"="plot3d")) }) output$usermatrix <- renderTable({ # grab the user matrix from the par3d stored in our rglWidgetAux # note we are using two different "validate"s here, which is quite the pain if you # don''t notice that it is declared in two different libraries shiny::validate(need(!is.null(input$ctrlplot3d),"User Matrix not yet queried")) umat <- matrix(0,4,4) jsonpar3d <- input$ctrlplot3d if (jsonlite::validate(jsonpar3d)){ par3dout <- fromJSON(jsonpar3d) umat <- matrix(unlist(par3dout$userMatrix),4,4) # make list into matrix } return(umat) }) scenegen <- reactive({ # make a random scene input$regen n <- 1000 x <- sort(rnorm(n)) y <- rnorm(n) z <- rnorm(n) + atan2(x, y) plot3d(x, y, z, col = rainbow(n)) scene1 <- scene3d() rgl.close() # make the app window go away return(scene1) }) output$plot3d <- renderRglwidget({ rglwidget(scenegen()) }) } shinyApp(ui=ui, server=server)

Y finalmente aquí está lo que parece:

Tenga en cuenta que lo configuro para que pueda agregarle comandos, puede (probablemente) cambiar los parámetros y cualquier otra cosa con un control basado en este.

También tenga en cuenta que la estructura de par3d aquí (convertida a json y luego a R desde el javascript de rglwidget ) y la que está en rgl no son del todo idénticas, así que por ejemplo tuve que aplanar el userMatrix ya que WebGL parece preferirlo como una lista de nombres como opuesto a las otras matrices que surgieron como se esperaba.