---
title: "Statistical and annotation WebGL layers"
output:
  rmarkdown::html_vignette:
    toc: true
vignette: >
  %\VignetteIndexEntry{Statistical and annotation WebGL layers}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r setup, 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
)

library(ggplot2)
library(ggWebGL)
```

This vignette covers statistical summaries, curve/contour layers, filled
regions, raster grids, annotations, and fixed-scale facets. Evaluation is
disabled during CRAN, package checks, and CI unless explicitly enabled with
`GGWEBGL_EVAL_COVERAGE_VIGNETTE=true` or `NOT_CRAN=true`. Live WebGL widgets
are additionally disabled unless `GGWEBGL_EVAL_LIVE_WIDGETS=true` is set; rich
local or pkgdown builds should set both `GGWEBGL_EVAL_COVERAGE_VIGNETTE=true`
and `GGWEBGL_EVAL_LIVE_WIDGETS=true`.

```{r coverage-eval-note, echo = FALSE, results = "asis", eval = TRUE}
if (!ggwebgl_eval_code) {
  cat(
    "> Example chunks are shown but not evaluated in this build. ",
    "Set `GGWEBGL_EVAL_COVERAGE_VIGNETTE=true` or `NOT_CRAN=true` ",
    "to evaluate them during a local non-CI render.\n",
    sep = ""
  )
} else if (!ggwebgl_eval_widgets) {
  cat(
    "> Non-widget code chunks may evaluate in this build, but live WebGL ",
    "widgets are skipped. Set `GGWEBGL_EVAL_LIVE_WIDGETS=true` as well ",
    "to render browser-side widgets locally.\n",
    sep = ""
  )
}
```

# Curves and Contours

Frequency polygons, density curves, two-dimensional density contours, and
regular contour lines all serialize to line/path primitives. The statistical
work remains with `ggplot2`.

## Applet: Frequency polygons

```{r frequency-polygons, eval = ggwebgl_eval_widgets}
p <- ggplot(mtcars, aes(mpg, colour = factor(cyl))) +
  geom_freqpoly_webgl(binwidth = 4, linewidth = 1.1) +
  labs(title = "Frequency polygons")
ggplot_webgl(p, height = 420)
```

## Applet: Density curves

```{r density-curves, eval = ggwebgl_eval_widgets}
p <- ggplot(mtcars, aes(mpg, colour = factor(cyl))) +
  geom_density_webgl(linewidth = 1.1) +
  labs(title = "Density curves")
ggplot_webgl(p, height = 420)
```

## Code-only: Two-dimensional density contours

`geom_density2d_webgl()` uses `ggplot2`'s two-dimensional density statistic,
which requires `MASS` at render time. The chunk is skipped when that optional
runtime dependency is unavailable.

```{r density2d-contours, eval = ggwebgl_eval_widgets && requireNamespace("MASS", quietly = TRUE)}
p <- ggplot(mtcars, aes(wt, mpg)) +
  geom_density2d_webgl(linewidth = 0.8) +
  labs(title = "Two-dimensional density contours")
ggplot_webgl(p, height = 420)
```

## Code-only: Gridded contour lines

```{r gridded-contour-lines, eval = ggwebgl_eval_widgets}
volcano_df <- as.data.frame(as.table(volcano))
names(volcano_df) <- c("x", "y", "z")
volcano_df$x <- as.numeric(volcano_df$x)
volcano_df$y <- as.numeric(volcano_df$y)

p <- ggplot(volcano_df, aes(x, y, z = z)) +
  geom_contour_webgl(bins = 8) +
  labs(title = "Gridded contour lines")

ggplot_webgl(p, height = 420)
```

# Ranges and Summaries

Range and summary geoms combine segment, point, and rectangle primitives.

## Applet: Linerange and pointrange

```{r linerange-pointrange, eval = ggwebgl_eval_widgets}
summary_df <- data.frame(
  group = factor(c("a", "b", "c")),
  y = c(4.1, 5.3, 6.0),
  ymin = c(3.6, 4.7, 5.4),
  ymax = c(4.8, 6.1, 6.8)
)

p <- ggplot(summary_df, aes(group, y, ymin = ymin, ymax = ymax)) +
  geom_linerange_webgl(linewidth = 1.2) +
  geom_pointrange_webgl(colour = "#2563eb") +
  labs(title = "Linerange and pointrange")
ggplot_webgl(p, height = 420)
```

## Applet: Error bars and crossbars

```{r error-bars-crossbars, eval = ggwebgl_eval_widgets}
p <- ggplot(summary_df, aes(group, y, ymin = ymin, ymax = ymax)) +
  geom_errorbar_webgl(width = 0.25) +
  geom_crossbar_webgl(aes(fill = group), width = 0.45, alpha = 0.55) +
  labs(title = "Error bars and crossbars")
ggplot_webgl(p, height = 420)
```

## Applet: Boxplot summary

```{r boxplot-summary, eval = ggwebgl_eval_widgets}
p <- ggplot(mtcars, aes(factor(cyl), mpg, fill = factor(cyl))) +
  geom_boxplot_webgl() +
  labs(title = "Boxplot summary")
ggplot_webgl(p, height = 420)
```

## Applet: Violin density summary

Violin rendering uses `ggplot2`'s built density output.

```{r violin-density-summary, eval = ggwebgl_eval_widgets}
p <- ggplot(mtcars, aes(factor(cyl), mpg, fill = factor(cyl))) +
  geom_violin_webgl(alpha = 0.7) +
  labs(title = "Violin density summary")
ggplot_webgl(p, height = 420)
```

# Filled Regions

Ribbon, area, and simple polygon layers are filled-region APIs. They are useful
for compact displays, but complex polygon topology such as holes or
self-intersections should be treated as outside the current core contract.

## Applet: Ribbon band

```{r ribbon-band, eval = ggwebgl_eval_widgets}
band <- data.frame(
  x = seq(0, 2 * pi, length.out = 80)
)
band$y <- sin(band$x)
band$ymin <- band$y - 0.15
band$ymax <- band$y + 0.15

p <- ggplot(band, aes(x, ymin = ymin, ymax = ymax)) +
  geom_ribbon_webgl(fill = "#93c5fd", alpha = 0.6) +
  geom_line_webgl(aes(y = y), colour = "#1d4ed8") +
  labs(title = "Ribbon band")
ggplot_webgl(p, height = 420)
```

## Code-only: Area band

```{r area-band, eval = ggwebgl_eval_widgets}
p <- ggplot(band, aes(x, y)) +
  geom_area_webgl(fill = "#a7f3d0", alpha = 0.7) +
  labs(title = "Area band")
ggplot_webgl(p, height = 420)
```

## Applet: Simple polygon

```{r simple-polygon, eval = ggwebgl_eval_widgets}
triangle <- data.frame(
  x = c(0, 1, 0.2),
  y = c(0, 0.2, 1),
  group = 1
)

p <- ggplot(triangle, aes(x, y, group = group)) +
  geom_polygon_webgl(fill = "#f97316", alpha = 0.7) +
  labs(title = "Simple polygon")
ggplot_webgl(p, height = 420)
```

# Raster and Annotation Layers

Raster layers are intended for regular cell displays. Text and label layers are
overlay metadata, not full WebGL glyph rendering.

## Applet: Raster grid

```{r raster-grid, eval = ggwebgl_eval_widgets}
small_raster <- expand.grid(
  x = seq_len(8),
  y = seq_len(6),
  KEEP.OUT.ATTRS = FALSE
)
small_raster$value <- with(small_raster, x * y)

p <- ggplot(small_raster, aes(x, y, fill = value)) +
  geom_raster_webgl() +
  labs(title = "Raster grid")
ggplot_webgl(p, height = 420)
```

## Applet: Text and rug overlays

```{r text-and-rug-overlays, eval = ggwebgl_eval_widgets}
labels <- data.frame(
  x = c(2, 6),
  y = c(2, 5),
  label = c("low", "high")
)

p <- ggplot(small_raster, aes(x, y, fill = value)) +
  geom_tile_webgl(alpha = 0.55) +
  geom_text_webgl(data = labels, aes(x = x, y = y, label = label), inherit.aes = FALSE) +
  geom_rug_webgl(colour = "#334155") +
  labs(title = "Text and rug overlays")
ggplot_webgl(p, height = 420)
```

## Code-only: Label overlay metadata

```{r label-overlay-metadata, eval = ggwebgl_eval_widgets}
p <- ggplot(labels, aes(x, y, label = label)) +
  geom_label_webgl(fill = "#ffffff", colour = "#0f172a") +
  labs(title = "Label overlay metadata")
ggplot_webgl(p, height = 420)
```

# Facets, Coordinates, and Fallbacks

Fixed-scale facets are supported across the primitive families. Free-scale
facets are serialized conservatively as metadata unless panel-local scaling is
available for the layer combination being rendered.

## Code-only: Fixed-scale facets

```{r fixed-scale-facets, eval = ggwebgl_eval_widgets}
p <- ggplot(mtcars, aes(wt, mpg, colour = factor(cyl))) +
  geom_point_webgl() +
  facet_wrap(~am) +
  labs(title = "Fixed-scale facets")

ggplot_webgl(p, height = 420)
```

Unsupported geoms remain visible to the extraction layer as unsupported metadata
rather than being silently claimed as WebGL primitives. When a plot needs exact
`ggplot2` parity for an unsupported layer, keep that layer in a static ggplot or
replace it with one of the supported WebGL primitives above.
