---
title: "Experiment with Renderer Capabilities"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Experiment with Renderer Capabilities}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r, include = 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/future-work-gallery.R",
  file.path("..", "inst", "examples", "htmlwidget", "future-work-gallery.R"),
  system.file("examples", "htmlwidget", "future-work-gallery.R", package = "ggWebGL")
)
example_candidates <- example_candidates[nzchar(example_candidates) & file.exists(example_candidates)]
if (!length(example_candidates)) {
  stop("Could not find future-work-gallery.R")
}
sys.source(example_candidates[[1L]], envir = knitr::knit_global())

renderer_capability_widgets <- if (ggwebgl_eval_widgets) {
  future_work_demo_widgets()
} else {
  NULL
}
```

# Boundary

This vignette demonstrates renderer capabilities in `ggWebGL`.
The examples are renderer-generic: they exercise primitive payloads, interaction
state, camera state, and widget controls without assigning scientific or
package-specific meaning to the data.

The showcased paths are:

- `vectors` layers for 2D and 3D arrow glyphs
- stable point ids for brush and lasso selection callbacks
- linked magnifier metadata for brush-driven local zoom views
- timeline metadata for frame-filtered rendering
- opt-in 3D camera options for point, line, and vector layers
- first-class structured-grid surface payloads and arbitrary mesh payloads

The live widgets are sourced from `future_work_vector_field_demo()`,
`future_work_selection_demo()`, `future_work_timeline_demo()`,
`future_work_3d_camera_demo()`, and `future_work_mesh_surface_demo()` in
`inst/examples/htmlwidget/future-work-gallery.R`.

This article is intended for visual inspection
and API orientation, not for performance claims. Fixed-rate interaction numbers
must come from the benchmark scripts before they are used in package material.

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.** The capabilities in this vignette are exported and tested, but they
are documented as `Experimental`: vectors, brush/lasso selection, linked
magnifier metadata, timeline controls, 3D camera state, mesh/surface rendering,
and compact transport may still evolve with the renderer contract.

# Vector Arrows

**Primitive contract.** The vector demo sends `x`, `y`, optional `z`, `xend`,
`yend`, optional `zend`, `width`, `head_size`, `rgba`, and stable ids through
`ggwebgl_layer_vectors()`.

**What to inspect.** Arrow shafts and heads should remain contained in the plot
region during pan and zoom. The arrows are a renderer primitive, not a line-layer
approximation.

```{r vector-arrows, out.width='100%', eval = ggwebgl_eval_widgets}
renderer_capability_widgets$vectors
```

# Brush and Lasso Selection

**Primitive contract.** The selection demo uses point payloads with stable
`id` values and enables the widget-owned `brush` and `lasso` interaction modes.

**What to inspect.** Dragging a brush or lasso region should show the selection
outline, highlight selected samples, update the selected-count status, and
report selected point ids through Shiny-style events or an optional JavaScript
callback when the widget is embedded in an application.

```{r selection-ids, out.width='100%', eval = ggwebgl_eval_widgets}
renderer_capability_widgets$selection
```

# Linked Magnifying-Glass Zoom

**Primitive contract.** The linked zoom demo uses `ggwebgl_magnify_region()`
with `interactive = TRUE`. The helper builds a two-panel renderer spec, stores
the linkage in `render$links$magnifiers`, and enables a brush interaction on the
source panel. The right panel starts with a viewport that exactly matches the
selected rectangle on the left.

```{r linked-zoom-setup}
set.seed(2031)
linked_zoom_points <- data.frame(
  x = c(rnorm(900, -0.7, 0.28), rnorm(700, 0.85, 0.2), rnorm(600, 0.1, 0.18)),
  y = c(rnorm(900, 0.0, 0.2), rnorm(700, 0.55, 0.16), rnorm(600, -0.7, 0.12)),
  group = rep(c("global", "local", "bridge"), c(900, 700, 600))
)

linked_zoom_source <- ggwebgl_spec(
  layers = list(
    ggwebgl_layer_points(
      linked_zoom_points,
      x = "x",
      y = "y",
      colour = c("#2563eb", "#f97316", "#0f766e")[match(linked_zoom_points$group, c("global", "local", "bridge"))],
      alpha = 0.36,
      size = 2.2
    )
  ),
  labels = list(title = "Interactive linked zoom"),
  webgl = list(shader = "density_splat", interactions = character())
)

linked_zoom_spec <- ggwebgl_magnify_region(
  linked_zoom_source,
  region = list(x = c(0.48, 1.25), y = c(0.25, 0.82)),
  display = "panel",
  interactive = TRUE,
  global_label = "Global view",
  zoom_label = "Brush-driven zoom"
)

linked_zoom_spec$render$links$magnifiers[[1L]]
```

**What to inspect.** Drag a brush rectangle in the left panel. The rectangle is
drawn in global coordinates, selected points are highlighted, and the right
panel updates live to the brushed data-coordinate region. This is a renderer
link, not a backend semantic selection.

```{r linked-zoom-widget, out.width='100%', eval = ggwebgl_eval_widgets}
ggWebGL(linked_zoom_spec, height = 430)
```

# Timeline Controls

**Primitive contract.** The timeline demo attaches `frame` metadata to points
and passes a `ggwebgl_timeline()` specification through `ggwebgl_spec()`.

**What to inspect.** The play, scrub, speed, and reset controls should change
which exact frame is visible without changing the underlying scene contract.

```{r timeline-controls, out.width='100%', eval = ggwebgl_eval_widgets}
renderer_capability_widgets$timeline
```

# Opt-In 3D Camera

**Primitive contract.** The 3D demo uses point, line, and vector layers with
`z` coordinates and sets `webgl$view` with an explicit `ggwebgl_view()` camera
controller and projection.

**What to inspect.** Dragging the scene should change the camera orientation
while ordinary two-dimensional plots continue to use the existing pan and zoom
behavior. Trackball examples use distinct rotation state rather than the orbit
controller alias.

```{r camera-3d, out.width='100%', eval = ggwebgl_eval_widgets}
renderer_capability_widgets$camera_3d
```

# Mesh and Surface Helpers

**Primitive contract.** The surface demo sends a regular height field as a
first-class `surface` payload through `ggwebgl_layer_surface()`. Direct arbitrary
triangle payloads remain the separate `mesh` contract through
`ggwebgl_layer_mesh()`.

**What to inspect.** The surface is rendered as indexed triangle geometry with
generated normals, Lambert material metadata, and an optional wireframe overlay.
Unstructured meshes use the separate indexed mesh path with scalar colouring,
wireframe edges, and renderer-owned face/vertex picking ids. Raster fields
remain raster fields; they are not silently treated as surfaces.

```{r mesh-surface, out.width='100%', eval = ggwebgl_eval_widgets}
renderer_capability_widgets$mesh_surface
```

# Standalone Gallery

The same examples can be exported as standalone HTML files:

```{r eval = FALSE}
source(system.file("examples", "htmlwidget", "future-work-gallery.R", package = "ggWebGL"))
export_future_work_gallery()
```
