Finding your way around ivue

ivue turns supplied coordinates and annotations into interactive views. Start with what you want to show, then choose an entry point. The example data and recipes guide supplies small, reproducible inputs. The introduction develops plotting controls, the retinal case study explains a real dataset, and the animation guide covers longer recorded sequences.

Choose a starting point

Your task Start here Decision
Inspect a point cloud plot3D.plain(X) One color or explicit row colors; no annotation legend.
Show a numerical annotation plot3D.cont(X, values) Continuous or explicitly binned colors with a matching legend.
Show categories plot3D.groups(X, groups) Named groups, which need not be clusters.
Inspect an embedded graph prepare.graph() then plot3D.graph() Check IDs and edges before adding coordinates and annotations.
Compare views consistently A reusable color scale and camera.zup() Fix the meaning of colors and the initial orientation.
Add geometric context layers = list(...) Supply connectivity, labels, a mesh, a reference surface, or axes.
Play recorded coordinates animate.frames() Preserve row identity across all frames.
Share a view htmlwidgets::saveWidget() or write.animation.gif() Choose interactive HTML or a fixed raster animation.

A first view

This illustrative helix uses base R and a numerical annotation: height. No embedding or model is fitted.

t <- seq(0, 2 * pi, length.out = 24)
X <- cbind(x = cos(t), y = sin(t), z = seq(-1, 1, length.out = 24))
rownames(X) <- sprintf("point-%02d", seq_len(nrow(X)))
height <- X[, "z"]
stopifnot(ncol(X) == 3L, all(is.finite(X)), length(height) == nrow(X))
height.scale <- color.scale.cont(height, limits = c(-1, 1))
head(map.colors(height, height.scale)$colors)
#> [1] "#4B0055" "#4A1060" "#46236A" "#3F3274" "#33407D" "#1D4E85"
view <- plot3D.cont(X, values = height, scale = height.scale,
                   legend.title = "Height", legend.width = 120,
                   point.size = 7, height = 340,
                   camera = camera.zup(zoom = 0.65))
view

Interactive 3D view of 24 observations.

Read the view: color records height along the illustrative helix; it does not identify a fitted trajectory. Drag to inspect its geometry.

The assignment constructs a widget on a private rgl null device. The final view expression displays it in this document; interactive printing uses RStudio’s Viewer or a browser. Construction itself opens neither a browser nor a native graphics window. Saving is a third operation:

htmlwidgets::saveWidget(view, "view.html", selfcontained = FALSE)

This leaves view.html and view_files/ in your working directory. Keep them together when sharing; open view.html in a browser. A self-contained HTML file is another option when Pandoc is available.

If rgl is absent, the widget and save chunks are skipped; data preparation and color mapping still run. Reading an already rendered vignette needs neither rgl nor R, but interacting with its scene requires a browser with WebGL.

For details: observation identity, function catalog, or optional packages and help.

Keep observations aligned

Static plots require a numeric matrix or all-numeric data frame with exactly three columns, at least one row, and finite coordinates. Each row is an observation. For a planar static plot, explicitly append z = 0 to two-column coordinates. Missing coordinates, NaN, and infinity cause errors; rows are never silently cleaned or dropped.

Named point-cloud values, groups, per-point colors, style color vectors, and logical highlight masks match observation IDs in rownames(X). Explicit row names must be unique, nonempty, and nonmissing. Annotation names must match the complete ID set exactly; partial, duplicate, missing, or extra names fail. Automatic data-frame row numbers are not observation IDs. Named annotations without explicit coordinate IDs also fail. Unnamed vectors follow row position; use unname() explicitly when their names are not IDs. Only unnamed scalar colors are recycled. Coordinates keep their original order, and numeric highlight indices and indexed layers still refer to those row positions. See the recipes. Missing numerical annotations use na.color; infinite annotation values are errors. Missing categories also use the missing color. Neither removes coordinate rows. Plain colors must be valid R colors with no missing entries, of length one or the number of rows.

For graph plots, vertex IDs are unique, nonempty, nonmissing strings. Unnamed coordinates and annotations follow graph$vertices$id order. Named coordinate rows, values, groups, per-vertex col, style color vectors, and logical highlight masks must match that entire ID set exactly and are reordered to graph order. Partial, duplicate, missing, or extra names fail. Numeric highlight indices and all indexed layers refer to graph vertex order after alignment, not to the supplied coordinate order. Never reorder a prepared vertex table without remapping its integer edge endpoints.

Highlighting changes styling, not membership or the fitted scale. Supply a logical mask without missing entries or one-based row indices; NULL selects all observations. highlight.style and non.highlight.style override point type, size, radius, color, or alpha. Style color vectors align to all rows. The legend describes the base scale with global alpha; it does not describe highlight overrides. See groups and highlighting.

Function catalog

This catalog covers all 18 explicit public function exports, once each. ivue registers 2 S3 methods: print() summarizes prepared graphs and color scales without rendering; $vertices, $edges, and scale fields retain the full data. These methods are described through their object workflows, not counted as separate function rows. ivue re-exports no functions. Dots in names such as plot3D.cont do not make them registered methods of plot3D() or plot(). Call these functions directly. The returned widgets use printing and knitting methods supplied by htmlwidgets/rgl; ivue does not add a separate widget printing interface. Prepared graphs, scales, and layers are lists carrying classes, inspected with ordinary R list operations.

In an R session, every name below has help, for example help("plot3D.cont", package = "ivue"). Shared help topics describe related functions together. Internal dot-prefixed helpers are implementation details. The repository’s make audit-guide checks catalog coverage and help aliases.

Point clouds and annotations

Function Purpose Principal input Returned object
plot3D.plain() Draw supplied point colors. Finite n-by-3 X, scalar or row colors. rglwidget/htmlwidget.
plot3D.cont() Draw numerical colors and a legend. X, one numerical value per row, optional scale. Widget with mapping metadata.
plot3D.groups() Draw categorical colors and a legend. X, one group per row, optional scale. Widget with mapping metadata.

attr(widget, "ivue") records coordinates, integer row.ids, explicit observation.ids (or NULL for unnamed coordinates), mapped colors, highlight, draw IDs, aspect, camera, and the captured scene. Draw IDs describe serialized scene objects, not a device left open for further drawing.

Graph preparation and plotting

Function Purpose Principal input Returned object
prepare.graph() Validate topology and expose vertex/edge order. Graph table, lists, matrix, or igraph object. ivue_graph list: vertices, edges, directedness, weight type.
plot3D.graph() Draw an embedded graph with optional annotations. Graph plus exactly one of X or layout. Widget with prepared graph metadata.

Accepted formats are an edge data frame with from, to, weight and an explicit vertices argument (including isolates); a list with edges and vertices; paired adj.list/weight.list lists whose neighbors are integer row indices; dense square adjacency matrices; Matrix sparse adjacency matrices; and igraph objects. A prepared ivue_graph can be reused and is revalidated. Vertex attributes are retained. Table edges retain input order; reciprocal undirected list/matrix edges retain one copy from the lower vertex index. Edge widths and colors follow this prepared edge order.

Matrices use zero for absence; use tables/lists for actual zero-weight edges. Explicit sparse zeros are rejected. Undirected adjacency must be reciprocal with equal weights. Self-loops and parallel edges are rejected, and directed data can be prepared but cannot be rendered in this release.

With supplied X, ivue does not compute a layout or construct an igraph object for table/list/matrix inputs. Built-in layout = "kk" and "fr" require igraph: Kamada-Kawai uses positive distances, whereas Fruchterman-Reingold uses positive strengths. Declare weight.type accordingly; no inversion is performed. "unweighted" permits only unit or missing weights. Missing weights otherwise fail. Finite zero/negative weights can be stored and drawn with supplied coordinates, but not used by these layout adapters. Weights never automatically set visual edge width or color. A custom layout function receives the prepared graph and returns n-by-3 coordinates. ivue provides no general embedding or graph-construction API. See the weighted graph workflow.

Reusable color scales and legends

Function Purpose Principal input Returned object
color.scale.cont() Fit a continuous or binned numerical scale. Reference values, optional limits, palette, center or breaks. ivue_color_scale list.
color.scale.groups() Fix category ordering and colors. Reference groups/factor and optional named colors. ivue_color_scale list.
map.colors() Apply a fitted scale without plotting. Values/groups and their scale. List with row-aligned colors, legend data frame, and scale.

Continuous scales use the reference range by default; limits fixes it. center requires a supplied diverging palette or color.map and makes automatic limits symmetric about it. For a palette, it places the center at the palette midpoint. A custom color.map instead receives data-unit values unchanged by centering; with explicit limits, center does not alter callback colors. With explicit limits, the center must lie strictly inside them. For constant or all-missing reference inputs, see the scale help.

Out-of-range values are squished to the endpoints by default. oob = "censor" uses na.color; oob = "error" rejects them. Missing values use na.color (default "gray80"). No default winsorization occurs. In mode = "binned", choose uniform or quantile bins, or explicit increasing breaks; the intervals are right-closed, with the lowest endpoint included. The scale help gives the contracts for explicit breaks, winsorization, and legend precision.

Factors retain level order, including unused nonmissing levels. Other group vectors use first-occurrence order. When plots fit a default categorical scale, this is the supplied annotation order before ID alignment, for both point and graph views. Reuse a scale when annotation order or membership changes. Named colors must cover the reference levels; unknown groups error unless unknown = "missing". Missing values are distinct from a literal "NA" category; empty strings are valid groups. Numeric palette indices are resolved when fitting scales. A color.map function runs at mapping time on data-unit values after out-of-range handling. It must return one color per value and be deterministic and pointwise: the same value must have the same color when mapped alone, reordered, or in another batch. Observations, legend ticks, and the ramp are separate calls. function(x) ifelse(x < 0, "blue", "red") obeys that contract; recomputing range(x) or ranks inside each call does not, even without mutable external state. Prefer a palette with fixed limits for ordinary comparisons. Supply color.map or palette, not both.

map.colors() returns a legend with label, color, and count. Continuous ticks have missing counts; binned/category counts describe the mapped input. A missing entry appears when needed. Reuse one scale across related views: separately autoscaled panels can give the same color to different numbers. The shared-view recipe checks this explicitly. Animation accepts a map.colors() result through mapping to retain its legend; caption explains what the fixed colors mean across frames.

Geometric layers

Function Purpose Principal input Returned object
layer3D.edges() Add straight segments. Two-column matrix of one-based endpoint indices. ivue_layer specification.
layer3D.path() Connect observations in a chosen order. Ordered one-based row indices. ivue_layer specification.
layer3D.labels() Label selected observations. Row indices and equally many text labels. ivue_layer specification.
layer3D.mesh() Add supplied triangular faces. Three-column matrix of one-based vertex indices. ivue_layer specification.
layer3D.surface() Add an independent gridded height surface. Monotone x, y, and finite z matrix. ivue_layer specification with its own coordinates.
layer3D.axes() Add axes intersecting at an origin. Optional origin, limits, labels, and styles. ivue_layer specification.
layer3D.callback() Run advanced rgl drawing before capture. Function of scene context and named additional arguments. ivue_layer specification.

Combine specifications with layers = list(edge.layer, label.layer, ...) in a plotting call. Edges, paths, labels, and meshes use plotting rows (aligned graph rows for graph plots). Label offset has three components in coordinate units. Mesh colors are per face and do not inherit point colors. Meshes retain supplied connectivity and do not repair folds or degenerate geometry.

A surface has its own grid: z[i, j] belongs to (x[i], y[j]), so z has length(x) rows and length(y) columns. Both axes are strictly monotone; coordinates must be finite. Grid cells split into planar triangles. No alignment or rescaling occurs. Surface bounds contribute to the scene, but automatic origin-axis limits use the plotted observations. See the introduction for meshes and reference surfaces.

layer3D.axes() crosses at c(0, 0, 0) by default, with positive arrowheads and no ticks. It is distinct from ordinary bounding-box axes enabled by axes = TRUE; normally use axes = FALSE with this layer. Its 3-by-2 limits set endpoints, not clipping. The camera is unchanged.

Callbacks run once during scene construction on the private rgl device, before serialization, not on later browser events. Their first argument contains X, integer row.ids, observation.ids, colors, highlight, and draw.ids; args supplies named additional arguments. They must not open, close, or switch devices. Captured object IDs are not live devices. Constructing the specification does not run the callback; drawing its plot does. Prefer ordinary layer constructors when they express the intended geometry.

Cameras and aspect ratios

Function Purpose Principal input Returned object
camera.zup() Specify an initial z-up view. Elevation, turn, field of view, zoom. List with 4-by-4 userMatrix, fov, zoom.

point.size measures screen pixels. point.type = "sphere" instead uses sphere.radius in coordinate units (default one percent of the largest span, with a minimum of 1e-8). Setting radius alone does not select spheres. Spheres carry spatial extent and can overlap; large scenes cost more to draw. aspect = "equal" preserves equal units along all axes. "normalized" stretches axes independently, changing relative distances and proportions.

fov = 0 is orthographic: distance from the camera does not shrink an object. Positive field of view introduces perspective foreshortening. Static plots default to camera.zup(elevation = 20, turn = -135, fov = 0, zoom = 0.8). Away from the poles, positive z initially projects upward. Interactive rotation can tilt it; this is not an enforced rotation constraint. Explicit theta, phi, or userMatrix selects the alternative camera controls described in help("plot3D.plain", package = "ivue"). Browser rotation does not update R-side camera metadata or synchronize another widget. Open View controls for keyboard rotation/zoom and Reset view. Download view settings saves an R recipe containing the current camera, bounds, and aspect. Source it and supply those settings explicitly to another plot:

source("ivue-view.R")
recovered <- plot3D.plain(X, camera = view$camera, limits = view$limits,
                          aspect = view$aspect)

The file contains viewing settings, not coordinates, annotations, or layers. Use matching widget dimensions as well for equal screen scale. The camera is transferable, but a bounds range from one scene may not contain another dataset. limits = rbind(c(-2, 2), c(-2, 2), c(-2, 2)) fixes the x/y/z framing range; all observations must fit. Spheres and layers cannot enlarge it, and geometry outside the range may fall outside the viewport. It is not a clipping box. See the common-bounds recipe.

Recorded-coordinate animation and export

Function Purpose Principal input Returned object
animate.frames() Play recorded positions with timeline/speed controls. At least two identically shaped n-by-2 or n-by-3 matrices; optional fixed edges. Widget with player and ivue.animation metadata.
write.animation.gif() Export retained animation frames. An animation widget and .gif destination. Normalized output path, invisibly.

Frame rows keep stable observation identity. If row names are present, every frame must have the same unique, nonempty names in the same order; frames are not automatically reordered. Each row must be entirely finite or entirely missing (NA/NaN), meaning inactive. Partially missing rows and infinity fail. Incident edges are hidden while either endpoint is inactive. An empty frame is allowed, but the whole sequence must contain a finite point. Two columns are embedded at z = 0 with a face-on default camera; three use z-up.

labels means one string per original frame, not vertex labels. frame.index selects increasing original indices and overrides max.frames (default 100 evenly spaced frames, retaining endpoints). Bounds use all original frames, even those omitted. Retained frames have equal playback duration. These may represent algorithm iterations or physical measurements, but the player does not infer elapsed time, interpolate, or align coordinates. Camera rotation changes the view, not the recorded positions. The animation guide distinguishes these operations in detail.

Output Controls and projection Dependencies and files
Interactive HTML Rotation and animation controls; initial orthographic or perspective view. htmlwidgets::saveWidget() writes HTML. selfcontained = TRUE needs Pandoc and embeds dependencies; FALSE writes a companion directory that must travel with the HTML. Viewing requires WebGL, not a running R session.
GIF Fixed initial orthographic camera; no interaction. Browser rotations/speed changes are not returned to R. write.animation.gif() needs magick and the R animation object, not saved HTML. Writes one GIF, using a separate raster renderer rather than a WebGL screenshot.

GIF controls are fps (0.1–100), width/height (64–8192 pixels), final.hold (0–600 additional seconds), loop, labels, and overwrite. The parent directory must exist and existing files are protected by default. annotations = TRUE adds the retained mapping legend and caption outside the scene; use larger dimensions for long text. Smaller camera zoom values enlarge both browser and GIF views. GIF delays round to centiseconds; points and edges need not look pixel-identical to WebGL, especially at intersections. Perspective cameras are rejected. selfcontained and libdir belong to htmlwidgets::saveWidget(), not GIF export. Neither export operation automatically captures later browser interaction; download view settings and reconstruct the R widget to reuse it.

Optional packages and detailed help

Loading ivue does not load rgl or install anything. Color scales, mapping, cameras, layers, and table/list/dense-matrix graph preparation work without rgl. Sparse inputs need Matrix; igraph inputs and built-in layouts need igraph. Constructing static or animation widgets needs rgl. The optional geometry package can create triangulations in a recipe, but ivue’s mesh layer only needs supplied triangle indices. GRIP traces in the animation guide come from grip. Shiny integration uses rgl::rglwidgetOutput() and rgl::renderRglwidget(); these are not ivue exports.

help(package = "ivue")
help("ivue-package", package = "ivue")
help("color.scale.cont", package = "ivue")
vignette("example-data", package = "ivue")

The compact task index, short function descriptions, and links to detailed workflows take inspiration from Hmisc’s documentation and its overview/help organization. The categories here follow ivue’s smaller visualization API.