---
title: Introduction to NeuroSurf Data Structures
author: Bradley Buchsbaum
date: '`r Sys.Date()`'
output:
  rmarkdown::html_vignette:
    toc: yes
    toc_depth: 2.0
    css: albers.css
    includes:
      in_header: albers-header.html
vignette: |
  %\VignetteIndexEntry{Introduction to NeuroSurf Data Structures}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
params:
  family: teal
  preset: homage
resource_files:
- albers.css
- albers.js
- albers-header.html

---

This vignette introduces the fundamental data structures used in the `neurosurf` package for representing and working with brain surface geometries and associated data.

For visualization, see the companion vignettes:

- `vignette("displaying-surfaces")` for interactive 3D plots based on `plot()` and `view_surface()`.
- `vignette("interactive-surfaces")` for HTML-widget based exploration.
- `vignette("surface-figures")` for multi-view, publication-quality layouts built with `surface_plot()` and friends.

## Setup

First, let's load the necessary libraries and set up the environment.

```{r setup, include = FALSE}
# Check for required packages - skip vignette build if rgl unavailable
has_rgl <- requireNamespace("rgl", quietly = TRUE)
if (!has_rgl) {
  knitr::opts_chunk$set(eval = FALSE)
  message("Skipping vignette: rgl package not available")
}

if (requireNamespace("ggplot2", quietly = TRUE) && requireNamespace("albersdown", quietly = TRUE)) ggplot2::theme_set(albersdown::theme_albers(family = params$family, preset = params$preset))
knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>",
  warning = FALSE,
  message = FALSE,
  fig.width = 7,
  fig.height = 5,
  rgl.newwindow = TRUE,  # Required for setupKnitr hook to work properly
  webgl = TRUE          # Use WebGL for embedding rgl plots (if any)
)

if (has_rgl) {
  # Ensure rgl widgets embed correctly
  options(rgl.useNULL = TRUE, rgl.printRglwidget = TRUE)
  rgl::setupKnitr()       # Setup rgl hook for knitr (if needed)
}

library(neurosurf)
if (has_rgl) library(rgl) # For potential visualization examples
library(igraph) # For graph operations
library(Matrix) # For sparse matrices used in NeuroSurfaceVector

# Prepare paths to example data included in the package
white_lh_asc <- system.file("extdata", "std.8_lh.smoothwm.asc", package="neurosurf")
white_rh_asc <- system.file("extdata", "std.8_rh.smoothwm.asc", package="neurosurf")
```

```{r albers-classes, echo=FALSE, results='asis'}
cat(sprintf(
  paste0(
    '<script>document.addEventListener("DOMContentLoaded",function(){',
    'document.body.classList.remove("palette-red","palette-lapis","palette-ochre","palette-teal","palette-green","palette-violet","preset-homage","preset-study","preset-structural","preset-adobe","preset-midnight");',
    'document.body.classList.add("palette-%s","preset-%s");',
    '});</script>'
  ),
  params$family,
  params$preset
))
```

## `SurfaceGeometry`: Representing the Mesh

The core building block for any surface analysis is the geometry itself. In `neurosurf`, this is represented by the `SurfaceGeometry` class.

An object of this class encapsulates:

1.  **`mesh`**: An `rgl::mesh3d` object containing the raw vertex coordinates and the triangular faces that define the surface shape.
2.  **`graph`**: An `igraph` object representing the connectivity of the mesh vertices. Edges connect adjacent vertices along the surface.
3.  **`hemi`**: A character string indicating the hemisphere, typically "lh" (left) or "rh" (right).

### Loading a `SurfaceGeometry`

You can load a surface geometry from various file formats (Freesurfer binary, Freesurfer ASCII `.asc`, GIFTI `.gii`) using the `read_surf_geometry()` function.

```{r load-geometry}
# Load the example left hemisphere white matter surface (.asc format)
lh_geom <- read_surf_geometry(white_lh_asc)

# Display summary information about the loaded geometry
show(lh_geom)
```

### Accessing Geometry Properties

Several methods allow you to access information within the `SurfaceGeometry` object:

```{r access-geometry}
# Total number of vertices (nodes) in the geometry
length(nodes(lh_geom))

# Vertex coordinates as an N x 3 matrix; its dimensions and first 3 rows
vertex_coords <- coords(lh_geom)
dim(vertex_coords)
head(vertex_coords, 3)

# The underlying igraph connectivity object
neurosurf::graph(lh_geom)

# The underlying rgl mesh3d object, and the hemisphere label
class(lh_geom@mesh)
lh_geom@hemi
```

## `NeuroSurface`: Mapping Data to Geometry

Often, we want to associate data values (like cortical thickness, fMRI activation, etc.) with each vertex on the surface. The `NeuroSurface` class links a *single vector* of data to a `SurfaceGeometry`.

It contains:

1.  **`geometry`**: The associated `SurfaceGeometry` object.
2.  **`indices`**: An integer vector specifying *which* vertices in the geometry have corresponding data values. This allows for representing data defined only on a subset of the surface.
3.  **`data`**: A numeric vector containing the data values. Its length must match the length of `indices`.

### Creating a `NeuroSurface`

You typically create a `NeuroSurface` using its constructor, providing the geometry, indices, and data.

```{r create-neurosurface}
# Generate some example data (e.g., based on x-coordinate)
# Use all vertices from lh_geom
vertex_indices <- nodes(lh_geom)
example_data <- coords(lh_geom)[, 1] # Use x-coordinate as data

# Create the NeuroSurface object
lh_surf_data <- NeuroSurface(geometry = lh_geom, 
                               indices = vertex_indices, 
                               data = example_data)

# Display summary
show(lh_surf_data)
```

### Loading Data with `read_surf` (Optional Data File)

If your data is stored in a separate file (e.g., AFNI `.1D.dset`, NIML `.niml.dset`), you can load both the geometry and the data using `read_surf`. Here we create a temporary `.1D.dset` file for demonstration.

``` {r load-surf-with-data, eval=TRUE}
# 1. Prepare sample data for a subset of nodes
sample_nodes_indices_R <- sample(nodes(lh_geom), size = 500) # R indices (1-based)
sample_nodes_indices_0based <- sample_nodes_indices_R - 1     # 0-based for .1D file
sample_data <- rnorm(500) 

# 2. Create a temporary file
temp_dset_file <- tempfile(fileext = ".1D.dset")

# 3. Write data in AFNI .1D format (node_index value)
write.table(cbind(sample_nodes_indices_0based, sample_data), 
            file = temp_dset_file, 
            row.names = FALSE, 
            col.names = FALSE, 
            sep = " ")

# 4. Load geometry and the data from the temporary file
#    read_surf will match nodes in the file to the geometry
lh_surf_loaded <- read_surf(surface_name = white_lh_asc, 
                                surface_data_name = temp_dset_file)

# Display summary of the loaded NeuroSurface
show(lh_surf_loaded)

# Number of data points loaded, and how many are non-zero (should match size = 500)
length(lh_surf_loaded@data)
sum(lh_surf_loaded@data != 0)
```

### Accessing `NeuroSurface` Properties

```{r access-neurosurface}
# The geometry can be retrieved and matches the original
identical(geometry(lh_surf_data), lh_geom)

# The data vector, via direct slot access (lh_surf_data@data) or as.vector();
# its length and first few values
data_vec <- lh_surf_data@data
length(data_vec)
head(data_vec, 5)

# The associated vertex indices
index_vec <- indices(lh_surf_data)
length(index_vec)
head(index_vec, 5)
```

## `NeuroSurfaceVector`: Mapping Multiple Data Vectors

When you have multiple measurements per vertex (e.g., time series data, multiple features), the `NeuroSurfaceVector` class is used. It links a *matrix* of data to a `SurfaceGeometry`.

It contains:

1.  **`geometry`**: The associated `SurfaceGeometry` object.
2.  **`indices`**: An integer vector specifying *which* vertices have data (same as `NeuroSurface`).
3.  **`data`**: A `Matrix` (from the `Matrix` package, often sparse) where *rows* correspond to vertices (matching `indices`) and *columns* represent different measurements or time points.

### Creating a `NeuroSurfaceVector`

```{r create-neurosurfacevector}
# Create example matrix data (e.g., x, y, z coordinates as 3 'features')
# Use all vertices
num_vertices <- length(nodes(lh_geom))
vertex_indices_vec <- nodes(lh_geom)

# Create a dense matrix first
example_matrix_data <- coords(lh_geom) 

# Convert to a Matrix object (can be sparse or dense)
example_matrix <- Matrix(example_matrix_data)

# Create the NeuroSurfaceVector
lh_surf_vec <- NeuroSurfaceVector(geometry = lh_geom,
                                    indices = vertex_indices_vec,
                                    mat = example_matrix)

# Display summary
show(lh_surf_vec)
```

### Accessing `NeuroSurfaceVector` Properties

```{r access-neurosurfacevector}
# The geometry matches the original
identical(geometry(lh_surf_vec), lh_geom)

# The data matrix (sparse Matrix slot, or as.matrix()); its dimensions
data_mat <- lh_surf_vec@data
dim(data_mat)

# Standard matrix indexing: one vertex (row), or one column across vertices
data_mat[10, ]
head(data_mat[, 2])

# The associated vertex indices
length(indices(lh_surf_vec))
```

## Specialized NeuroSurface Classes

`neurosurf` also provides specialized classes that inherit from `NeuroSurface` and add features primarily for visualization:

*   **`LabeledNeuroSurface`**: Associates categorical labels and corresponding colors with vertices (useful for atlases or parcellations). Contains `labels` and `cols` slots.
*   **`ColorMappedNeuroSurface`**: Stores data along with a specific colormap (`cmap`), data range (`irange`), and thresholds (`thresh`) to pre-define how the data should be visualized.
*   **`VertexColoredNeuroSurface`**: Stores specific hex color codes directly for each vertex (`colors` slot), bypassing data mapping.

These classes facilitate plotting with pre-set visual parameters using the `plot()` method discussed in `vignette("displaying-surfaces")`.

## Next Steps

Now that you understand the core data structures, explore the visualization vignettes:

- `vignette("displaying-surfaces")` — render surfaces with RGL, overlay data, and snapshot to PNG.
- `vignette("interactive-surfaces")` — create interactive HTML widgets with `surfwidget()`.
- `vignette("surface-figures")` — build publication-quality multi-view figures with colourbars and atlas outlines.
