---
title: "Interactive WebGL Scatterplots with reglScatterplot"
author: "George Muñoz"
date: "`r Sys.Date()`"
package: reglScatterplotR
output:
  BiocStyle::html_document:
    toc: true
    toc_depth: 2
vignette: >
  %\VignetteIndexEntry{Interactive WebGL Scatterplots with reglScatterplot}
  %\VignetteEngine{knitr::knitr}
  %\VignetteEncoding{UTF-8}
---

```{r setup, include = FALSE}
knitr::opts_chunk$set(
    collapse = TRUE,
    comment = "#>",
    fig.align = "center",
    # The vignette is HTML, so htmlwidgets embed natively; disable knitr's
    # PhantomJS / webshot screenshot fallback so no extra system dep is
    # needed to build the vignette.
    screenshot.force = FALSE
)
```

# Introduction

`reglScatterplot` is a thin R wrapper around the JavaScript
[regl-scatterplot](https://github.com/flekschas/regl-scatterplot) library,
exposing it as an `htmlwidgets` widget. It renders **millions** of
two-dimensional points in the browser via WebGL, which makes it well suited
to:

* single-cell RNA-seq UMAP/t-SNE embeddings,
* spatial transcriptomics tissue maps,
* high-dimensional flow / mass cytometry projections,
* any large `(x, y)` scatter that ggplot2 would draw too slowly.

The widget works in:

* the **RStudio Viewer** pane when run interactively,
* **standalone HTML** files produced by `htmlwidgets::saveWidget()`,
* **Shiny** applications and interactive R Markdown documents,
* **Jupyter notebooks** running the `IRkernel` (e.g. VSCode's Jupyter extension).

# Installation

```{r install, eval = FALSE}
# (chunk intentionally not run during knit)
# From GitHub
remotes::install_github("george123ya/reglScatterplotR")

# Once accepted into Bioconductor
if (!requireNamespace("BiocManager", quietly = TRUE)) {
    install.packages("BiocManager")
}
BiocManager::install("reglScatterplotR")
```

```{r library}
library(reglScatterplotR)
```

# A first plot

The simplest call needs only a `data.frame` and the names of the two
coordinate columns.

```{r basic}
set.seed(1L)
df <- data.frame(
    x = rnorm(20000),
    y = rnorm(20000),
    cluster = sample(letters[1:6], 20000, replace = TRUE),
    score = runif(20000)
)

reglScatterplot(df, x = "x", y = "y")
```

# Categorical colour mapping

Pass a character or factor column name (or vector) to `colorBy`. A draggable
legend appears in the upper-right corner of the plot; clicking a legend
entry filters the data, and shift/ctrl-click extends the selection.

```{r categorical}
reglScatterplot(df,
    x = "x", y = "y", colorBy = "cluster",
    legendTitle = "Cluster"
)
```

# Continuous colour mapping

Numeric `colorBy` produces a continuous colour scale. Use `vmin`/`vmax`
to clip outliers; the strings `"p1"`, `"p99"` and so on select percentiles.

```{r continuous}
reglScatterplot(df,
    x = "x", y = "y", colorBy = "score",
    continuousPalette = "magma",
    vmin = "p1", vmax = "p99"
)
```

For log-fold-change-style data use `centerZero = TRUE` to force a symmetric
diverging scale.

# Working with Bioconductor data structures

`reglScatterplot()` works directly on a `SingleCellExperiment`. Pass the
name of a `reducedDim()` as `x`. `colorBy` and `groupBy` can name a
`colData()` column or a gene in `rownames()`. Gene values are read from the
`logcounts` assay by default; use `assay` to pick another one.

Here we wrap the bundled example data in a small `SingleCellExperiment`.

```{r sce}
library(SingleCellExperiment)
data(reglScatterExample)
ex <- reglScatterExample
sce <- SingleCellExperiment(
    assays = list(logcounts = t(as.matrix(ex["CD3D"]))),
    colData = ex[, c("celltype", "nCount", "percentMito")],
    reducedDims = list(UMAP = as.matrix(ex[, c("UMAP_1", "UMAP_2")]))
)
sce
reglScatterplot(sce, x = "UMAP", colorBy = "celltype")
```

Colouring by a gene reads its row from the assay:

```{r sce-gene}
reglScatterplot(sce, x = "UMAP", colorBy = "CD3D", vmax = "p99")
```

A `SpatialExperiment` works the same way. Use `x = "spatial"` to plot the
tissue coordinates from `spatialCoords()`:

```{r spe, eval = FALSE}
reglScatterplot(spe, x = "spatial", colorBy = "celltype")
```

`Seurat` objects and in-memory `AnnData` objects (from `anndataR`) are also
accepted. They need the optional `SeuratObject` and `anndataR` packages.

# Linking plots in Shiny

When several `reglScatterplot()` widgets share the same `plotId`s, you can
synchronise pan/zoom across them with `enableReglScatterplotSync()`:

```{r sync, eval = FALSE}
library(shiny)
ui <- fluidPage(
    fluidRow(
        column(6, reglScatterplotOutput("plotA")),
        column(6, reglScatterplotOutput("plotB"))
    ),
    actionButton("link", "Link cameras")
)
server <- function(input, output, session) {
    output$plotA <- renderReglScatterplot({
        reglScatterplot(df,
            x = "x", y = "y", colorBy = "cluster",
            plotId = "plotA"
        )
    })
    output$plotB <- renderReglScatterplot({
        reglScatterplot(df,
            x = "x", y = "y", colorBy = "score",
            plotId = "plotB"
        )
    })
    observeEvent(input$link, {
        enableReglScatterplotSync(c("plotA", "plotB"))
    })
}
shinyApp(ui, server)
```

# Network requirements

On first use the JavaScript bundles (`d3`, `regl-scatterplot`, `pickr`,
`html2canvas`, `jsPDF`) are loaded from a CDN (`esm.sh` and
`cdnjs.cloudflare.com`). Once loaded they are cached by the browser. A
helpful error message is displayed in-place if the network is unavailable.

# Session info

```{r sessionInfo, eval = TRUE}
sessionInfo()
```
