--- title: "Using ggseg3d in Shiny" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Using ggseg3d in Shiny} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r} #| label: setup #| include: false knitr::opts_chunk$set( collapse = TRUE, comment = "#>", eval = FALSE ) ``` ggseg3d widgets work in Shiny applications. This vignette covers the output and render functions, plus reactive updates. ```{r} #| label: load-packages library(shiny) library(ggseg3d) ``` ## Basic Shiny integration Use `ggseg3dOutput()` in the UI and `renderGgseg3d()` in the server: ```{r} #| label: basic-app ui <- fluidPage( titlePanel("Brain Atlas Viewer"), sidebarLayout( sidebarPanel( selectInput( "hemi", "Hemisphere", choices = c("left", "right", "both"), selected = "left" ) ), mainPanel( ggseg3dOutput("brain", height = "500px") ) ) ) server <- function(input, output, session) { output$brain <- renderGgseg3d({ hemi <- if (input$hemi == "both") NULL else input$hemi ggseg3d(hemisphere = hemi) |> pan_camera("left lateral") }) } shinyApp(ui, server) ``` ## Output sizing Set explicit dimensions in `ggseg3dOutput()`: ```{r} #| label: output-sizing ggseg3dOutput("brain", width = "100%", height = "600px") ``` Or use `set_dimensions()` in the render function: ```{r} #| label: render-dimensions renderGgseg3d({ ggseg3d() |> set_dimensions(width = 800, height = 600) }) ``` ## Reactive data Update the brain plot when data changes: ```{r} #| label: reactive-data server <- function(input, output, session) { brain_data <- reactive({ tibble( region = c("precentral", "postcentral", "insula"), value = runif(3) ) }) output$brain <- renderGgseg3d({ ggseg3d( .data = brain_data(), atlas = dk(), # nolint [object_usage_linter] colour_by = "value" ) |> pan_camera("left lateral") }) } ``` ## Updating camera and background Use `updateGgseg3dCamera()` and `updateGgseg3dBackground()` to modify an existing widget without re-rendering: ```{r} #| label: reactive-updates ui <- fluidPage( sidebarLayout( sidebarPanel( selectInput( "view", "View", choices = c( "left lateral", "left medial", "right lateral", "right medial" ) ), selectInput("bg", "Background", choices = c("white", "black", "grey")) ), mainPanel( ggseg3dOutput("brain") ) ) ) server <- function(input, output, session) { output$brain <- renderGgseg3d({ ggseg3d() |> pan_camera("left lateral") }) observeEvent(input$view, { updateGgseg3dCamera("brain", input$view) }) observeEvent(input$bg, { updateGgseg3dBackground("brain", input$bg) }) } ``` These updates are faster than re-rendering the entire widget. ## Complete example Here's a full app with data selection and dynamic updates: ```{r} #| label: complete-app library(shiny) library(ggseg3d) library(dplyr) example_data <- tibble( region = c( "precentral", "postcentral", "insula", "superior parietal", "inferior parietal", "supramarginal", "cuneus", "pericalcarine" ), thickness = c(2.5, 2.3, 3.1, 2.2, 2.4, 2.6, 1.8, 1.9), volume = c(8500, 7200, 6800, 9100, 8800, 7500, 4200, 3800) ) ui <- fluidPage( titlePanel("Brain Metrics Explorer"), sidebarLayout( sidebarPanel( selectInput("metric", "Metric", choices = c("thickness", "volume")), selectInput( "view", "Camera View", choices = c( "left lateral", "left medial", "right lateral", "right medial", "left superior", "left inferior" ) ), checkboxInput("edges", "Show edges", value = FALSE), selectInput("bg", "Background", choices = c("white", "black", "grey90")) ), mainPanel( ggseg3dOutput("brain", height = "600px") ) ) ) server <- function(input, output, session) { output$brain <- renderGgseg3d({ p <- ggseg3d( .data = example_data, atlas = dk(), # nolint [object_usage_linter] colour_by = input$metric, text_by = input$metric ) |> pan_camera(input$view) |> set_background(input$bg) if (input$edges) { p <- p |> set_edges("black") } p }) } shinyApp(ui, server) ``` ## Performance tips **Minimize re-renders**: Use `updateGgseg3dCamera()` and `updateGgseg3dBackground()` for camera and background changes instead of re-rendering. **Debounce reactive data**: If data updates frequently, use `debounce()` to avoid excessive re-renders. **Pre-compute data**: Do data transformations outside the render function when possible. **Limit regions**: Showing fewer regions renders faster. Filter your atlas if you only need specific structures.