The brilliant rglwidget application gets userMatrix to create another graph with the same rotation

I have a brilliant application and integrate rgl 3d-plot into it. I am using renderRglwidgetfrom the package rglwidgetto insert rgl graphics using webgl into my brilliant application.

In the application, the user can rotate the schedule. Now I want to save the rotation state, so userMatrix or modelMatrix for subsequent creation of a similar graph with the same rotation as the user left by the previous graph.

Here I read something about Java variables storing userMatrix and other parameters. Can I access them from my brilliant app (in R code)?

In rgl itself, I can use rotationMatrix <- rgl.projection()$modelor rotationMatrix <- par3d()$modelMatrixto store model rotation. But since in my case the graphic does not rotate in rgl, these functions do not help me.

I tried this:

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)

But par3d()$modelMatrixit doesn't seem to return anything.

+4
source share
1 answer

The reason rgl:par3d()does not return anything, because the packages rgldo not actually control the scene for brilliant. A javascript-based library rglwidgetthat uses WebGL, manages it, and you copy the scene to another very compatible GL library (maybe they even use the same compiled library, but I doubt it) and display it in brilliant form. Therefore, it rgl.dev()will not help you.

AFAIK, , rglwidget javascript, , , . , , , , , , , . - , , .

javascript, www, , .

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);

R/ . userMatrix . userMatrix, modelMatrix, , , .

, "app.R" . , "ui.R" "server.R", javascript .

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)

, , :

enter image description here

, , , () , .

, par3d ( json, R javascript rglwidget) rgl , , , userMatrix WebGL, , , , , .

+5

All Articles