2016-04-25 1 views
4

Ich habe eine glänzende App und integrieren ein rgl 3D-Plot hinein. Ich verwende renderRglwidget aus dem rglwidget Paket, um die rgl-Grafiken mit Webgl in meine glänzende App einzufügen.shiny app rglwidget erhalten userMatrix, um ein weiteres Grundstück mit der gleichen Rotation zu generieren

In der App kann der Benutzer die Grafik drehen. Jetzt möchte ich den Rotationszustand speichern, daher generiert die userMatrix oder modelMatrix später ein ähnliches Diagramm mit der gleichen Drehung wie der Benutzer den vorherigen Graphen verlassen hat.

Here Ich habe etwas über Java-Variablen gelesen, die die userMatrix und andere Parameter speichern. Kann ich über meine glänzende App (im R-Code) auf sie zugreifen?

In rgl selbst kann ich rotationMatrix <- rgl.projection()$model oder rotationMatrix <- par3d()$modelMatrix verwenden, um die Rotation des Modells zu speichern. Aber da in meinem Fall die Grafik nicht in rgl gedreht wird, helfen mir diese Funktionen nicht.

Was ich versucht, ist dies:

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) 

Aber par3d()$modelMatrix scheint nichts zurück.

+0

Ich sehe nicht, wie deine Shiny-App irgendetwas tut, um die Matrix anzuzeigen, aber ich weiß nicht, dass Shiny gut genug ist, um zu wissen, dass es falsch ist. Nur für den Fall, dass es in Ordnung ist: Die open3d() - Funktion gibt einen Wert der rgl-Gerätenummer zurück, die geöffnet wurde, und par3d() hat ein optionales Argument "dev", das ein bestimmtes Gerät abfragen kann. Vielleicht, wenn Sie diese verwenden, bekommen Sie, was Sie wollen. – user2554330

+0

Haben Sie ein Feedback für mich? –

+0

Bist du da? Hast du meine Antwort versucht? –

Antwort

5

Der Grund rgl:par3d() gibt nichts zurück ist, weil die rgl Pakete nicht wirklich die Szene für glänzend verwaltet. Die Javascript basierte rglwidget Bibliothek, die WebGL nutzt, verwaltet es, und Sie kopieren über die Szene in eine andere sehr kompatible GL-Bibliothek (vielleicht verwenden sie sogar die gleiche kompilierte Bibliothek, aber ich bezweifle es) und das in glänzendem anzeigen. So rgl.dev() wird dir nicht helfen.

AFAIK, es ist nicht leicht, diese Werte zu erhalten, da sie in rglwidget Javascript versteckt sind, aber ich wollte auf sie zu bekommen, so baute ich eine glänzende benutzerdefinierte Eingabesteuerung, die es tun kann. Es war ziemlich viel Arbeit, und es könnte einen einfacheren Weg geben, aber ich habe es nicht gesehen und weiß zumindest, wie man jetzt benutzerdefinierte Eingabesteuerungen für glänzend baut. Wenn jemand einen leichteren Weg kennt, bitte erleuchte mich.

Hier ist das Javascript, es geht in einen www Unterordner, den Sie im selben Verzeichnis wie Ihr glänzender Code speichern.

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

ist die R/glänzend Code. Es verwendet die übliche Testszene und verfügt über eine Schaltfläche zum Abfragen der Szenen userMatrix und zeigt diese in einer Tabelle an. Ich habe die userMatrix und nicht die modelMatrix verwendet, weil die ehemalige ist einfach mit der Maus zu ändern, so können Sie sehen, dass Sie die frischesten Werte erhalten.

Beachten Sie, dass der Name "app.R" nicht wirklich optional ist. Entweder Sie müssen das verwenden, oder teilen Sie die Datei in "ui.R" und "server.R", sonst wird die oben genannte JavaScript-Datei nicht importiert.

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) 

Und hier endlich ist, wie es aussieht:

enter image description here

Bitte beachte, dass ich es so einrichten, können Sie Befehle, um es hinzuzufügen, man konnte (wahrscheinlich) Ändern Sie Parameter und alles andere mit einer Steuerung, die auf diesem basiert.

Beachten Sie auch, dass die par3d Struktur hier (zu json umgewandelt und dann zu R aus dem rglwidget JavaScript) und die in rgl sind nicht ganz identisch, so zum Beispiel ich die userMatrix abzuflachen hatte als WebGL es zu bevorzugen scheint, als eine Namensliste im Gegensatz zu den anderen Matrizen, die wie erwartet kamen.

+0

Kein Kommentar zu diesem Thema? –

+0

Vielen Dank für Ihre Mühe! Ich denke, das sollte sich im rglwidget-Paket finden! Ich konnte es jedoch nicht testen. Wo genau muss ich die Javascript-Datei ablegen? Ich gebe es in .www relativ von der glänzenden Datei ein, aber der Browser kann es nicht finden. – nnn

+0

Stange. www nicht .www, oder? Arbeitete für mich mit Chrome oder dem RStudio Viewer. Wie bei jedem benutzerdefinierten Shiny-Eingabesteuerelement. –

Verwandte Themen