--- title: "Temporal and 3D trajectories with ggWebGL" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Temporal and 3D trajectories with ggWebGL} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r, include = FALSE} knitr::opts_knit$set(purl = FALSE) ggwebgl_truthy <- function(x) { tolower(x) %in% c("1", "true", "yes", "y") } ggwebgl_ci_vars <- c( "CI", "GITHUB_ACTIONS", "GITLAB_CI", "BUILDKITE", "TRAVIS", "APPVEYOR", "CIRCLECI", "JENKINS_URL" ) ggwebgl_is_ci <- any(vapply(Sys.getenv(ggwebgl_ci_vars), ggwebgl_truthy, logical(1))) ggwebgl_is_check <- nzchar(Sys.getenv("_R_CHECK_PACKAGE_NAME_")) ggwebgl_eval_code <- !ggwebgl_is_ci && !ggwebgl_is_check && ( ggwebgl_truthy(Sys.getenv("NOT_CRAN")) || ggwebgl_truthy(Sys.getenv("GGWEBGL_EVAL_COVERAGE_VIGNETTE")) ) ggwebgl_eval_widgets <- ggwebgl_eval_code && ggwebgl_truthy(Sys.getenv("GGWEBGL_EVAL_LIVE_WIDGETS")) knitr::opts_chunk$set(collapse = TRUE, comment = "#>", eval = ggwebgl_eval_code) if (file.exists("DESCRIPTION") && requireNamespace("pkgload", quietly = TRUE)) { pkgload::load_all(".", export_all = FALSE, helpers = FALSE, quiet = TRUE) } else if (file.exists("../DESCRIPTION") && requireNamespace("pkgload", quietly = TRUE)) { pkgload::load_all("..", export_all = FALSE, helpers = FALSE, quiet = TRUE) } else { library(ggWebGL) } example_candidates <- c( "inst/examples/htmlwidget/temporal-trajectories.R", file.path("..", "inst", "examples", "htmlwidget", "temporal-trajectories.R"), system.file("examples", "htmlwidget", "temporal-trajectories.R", package = "ggWebGL") ) example_candidates <- example_candidates[nzchar(example_candidates) & file.exists(example_candidates)] if (!length(example_candidates)) { stop("Could not find temporal-trajectories.R") } sys.source(example_candidates[[1L]], envir = knitr::knit_global()) ``` # Purpose `ggWebGL` can render ordered trajectory/path layers with timeline metadata. The examples below use deterministic in-memory data and browser-side WebGL through `htmlwidgets`; they do not download data or report performance numbers. The widgets autoplay by default and can be paused, scrubbed, or reset with the timeline controls. If a browser throttles a background tab, pressing **Play** resumes the timeline. Code examples are shown by default. Live WebGL widgets are disabled during CRAN, package checks, and CI. Rich local or pkgdown rendering requires `GGWEBGL_EVAL_COVERAGE_VIGNETTE=true` and `GGWEBGL_EVAL_LIVE_WIDGETS=true`. **Status.** Trajectory paths, trajectory shader encodings, exact/cumulative timeline controls, and Shiny timeline messages are `Experimental` public APIs. They are suitable for renderer evaluation and small examples, but should not be described as stable performance or animation guarantees. The relevant renderer pieces are: - `geom_path3d_webgl()` for ordered paths with optional `z`, `frame`, and `time` aesthetics. - `animation_spec()` / `ggwebgl_timeline()` for exact or cumulative timeline metadata. - `trajectory_age`, `trajectory_velocity`, and `trajectory_direction` shader modes for renderer-side trajectory visual encodings. - `input$_timeline` and `updateGgWebGLTimeline()` for Shiny timeline integration. # 2D Cumulative Trajectory This spiral is rendered as an ordered path with `frame` metadata and cumulative timeline filtering. The `trajectory_age` shader makes the time prefix visible as the timeline is scrubbed. ```{r spiral-trajectory, out.width='100%', eval = ggwebgl_eval_widgets} temporal_spiral_widget(height = 460) ``` # 3D Trajectory The helix uses the same path contract, but supplies `z` and `time` values. The view is opt-in 3D with an orbit camera. ```{r helix-trajectory, out.width='100%', eval = ggwebgl_eval_widgets} temporal_helix_widget(height = 460) ``` # Velocity and Direction Encodings Trajectory velocity and direction are renderer-side visual encodings computed from the serialized path coordinates and timeline values. They do not add application semantics to the data. ```{r velocity-trajectory, out.width='100%', eval = ggwebgl_eval_widgets} temporal_velocity_widget(height = 460) ``` ```{r direction-trajectory, out.width='100%', eval = ggwebgl_eval_widgets} temporal_direction_widget(height = 460) ``` # Exact Frame Samples Exact filtering is useful for point-like animated samples. This example shows only the samples whose `frame` equals the selected timeline value. ```{r exact-particles, out.width='100%', eval = ggwebgl_eval_widgets} temporal_exact_particles_widget(height = 460) ``` # Shiny Timeline Integration In Shiny, the browser emits the current timeline state as `input$_timeline`. Server code can update the browser-side timeline with `updateGgWebGLTimeline()`. ```{r shiny-timeline-snippet, eval = FALSE} library(shiny) library(ggWebGL) ui <- fluidPage( actionButton("restart", "Reset"), ggWebGLOutput("plot", height = "420px"), verbatimTextOutput("timeline_state") ) server <- function(input, output, session) { output$plot <- renderGgWebGL({ temporal_helix_widget(height = 460) }) observeEvent(input$restart, { updateGgWebGLTimeline(session, "plot", index = 1L, playing = FALSE) }) output$timeline_state <- renderPrint({ input$plot_timeline }) } if (interactive()) { shinyApp(ui, server) } ``` The repository also includes a compact optional app at `inst/examples/shiny/timeline-control-demo.R`. # Limitations Missing values and group boundaries break paths. Cumulative timeline filtering draws complete visible prefixes; exact path filtering does not interpolate partial hidden segments. Velocity and direction are renderer-side visual encodings. Shiny timeline events report renderer time/frame state; downstream applications remain responsible for interpreting those values.