---
title: "Special Syntax for Layout Axis"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Special Syntax for Layout Axis}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r, include = FALSE}
knitr::opts_chunk$set(
    collapse = TRUE,
    comment = "#>"
)
```

`ggalign` focuses on aligning observations across multiple plots. While it
builds on the `ggplot2` framework, there are key differences in how scales,
facets, and coordinates are handled in the layout axis. This vignette highlights
the differences in syntax between `ggalign` and `ggplot2`.

## Layout Axis
The layout axis refers to specific axes in multi-plots layout: 
- x-axis in vertical stack layouts

- x-axis in horizontal stack layouts

- x- and y axes in the heatmap body: In heatmap layouts, both the x and y axes are
used to align observations.

`ggalign` introduces special syntax and handling for these axes, differing from
the default behavior in `ggplot2`. These adjustments ensure that observations
are properly aligned and operations are user-friendly. 

```{r setup}
library(ggalign)
```

```{r setup_data}
set.seed(123)
small_mat <- matrix(rnorm(81), nrow = 9)
rownames(small_mat) <- paste0("row", seq_len(nrow(small_mat)))
colnames(small_mat) <- paste0("column", seq_len(ncol(small_mat)))
```

## Position Scales
We introduce some adjustments to better align with the layout concept in
`breaks`, `labels` and `expand`.

### breaks
`breaks` and `labels` are typically handled similarly to discrete scales, as we
focus on aligning observations (which should be regarded as discrete variables)
in the layout axis; however, a continuous scale can also be provided. 

`breaks` should be one of:

    - `NULL` for no breaks

    - `waiver()` for the default breaks (the full data index or `NULL` if no
    data names and `labels` is `waiver()`)

    - A character vector of breaks (rownames / colunames of the matrix).

    - A numeric vector of data index (must be an integer).

    - A function that takes the data limits or the data index as input and
    returns breaks as output. Also accepts rlang lambda function notation. 

```{r}
ggheatmap(small_mat) + scale_x_continuous(breaks = NULL)
```

```{r}
ggheatmap(small_mat) + scale_x_continuous()
```

```{r}
no_names <- small_mat
colnames(no_names) <- NULL
ggheatmap(no_names) + scale_x_continuous()
```

```{r}
ggheatmap(small_mat) + scale_x_continuous(breaks = c("column3", "column5"))
```

```{r}
ggheatmap(small_mat) + scale_x_continuous(breaks = c(3, 5))
```

Floating number were not allowed in `breaks`.
```{r error=TRUE}
ggheatmap(small_mat) + scale_x_continuous(breaks = c(3.5, 5))
```

### labels
`labels` should be one of:

    - `NULL` for no labels

    - `waiver()` for the default labels (data names)

    - A character vector giving labels (must be same length as breaks)

    - An expression vector (must be the same length as breaks). See `?plotmath`
    for details. 

    - A function that takes the data names (or breaks if data has no names) as
    input and returns labels as output. This can be also a rlang lambda
    function.

```{r}
ggheatmap(small_mat) + scale_x_continuous(labels = NULL)
```

```{r}
ggheatmap(small_mat) + scale_x_continuous()
```

```{r}
ggheatmap(small_mat) +
    scale_x_continuous(labels = letters[seq_len(ncol(small_mat))])
```

```{r}
ggheatmap(small_mat) +
    scale_x_continuous(breaks = c(3, 5), labels = c("a", "b"))
```

### data ordering
Both `breaks` and `labels` should be provided in the original order of the raw
data, the internal will reorder them accordingly if you reorder the layout axis.

```{r}
index <- order(colMeans(small_mat))
xlabels <- letters[seq_len(ncol(small_mat))]
print(xlabels[index])

ggheatmap(small_mat) +
    scale_x_continuous(labels = xlabels) +
    hmanno("t") +
    align_order(index)
```

### expand
By default, we utilize zero expansion for the layout axis. This is typically the
desired setting. If you wish to introduce expansion, you must manually adjust it
and apply it to each plot to ensure proper axis alignment.
```{r}
ggheatmap(small_mat) +
    scale_x_continuous(expand = expansion(mult = 0.1)) +
    hmanno("t") +
    align_dendro(aes(color = branch), k = 3L) +
    scale_x_continuous(expand = expansion(mult = 0.1))
```

## theme
Although ggplot2 does not officially support vectorized input for theme
elements, we can still utilize it. For layout axis theme elements such as
`axis.text`, `axis.ticks`, and `axis.ticks.length`, these will be reordered
according to the layout axis ordering. 
```{r}
ggheatmap(small_mat) +
    theme(
        axis.text.x = element_text(
            colour = c(rep("red", 4), rep("blue", 5))
        ),
        axis.ticks.x = element_line(
            colour = c(rep("red", 4), rep("blue", 5))
        ),
        axis.ticks.length.x = unit(rep(c(1, 4), times = c(4, 5)), "mm")
    ) +
    hmanno("t") +
    align_dendro(aes(color = branch), k = 3L) +
    scale_y_continuous(expand = expansion()) &
    theme(plot.margin = margin())
```

To prevent the reordering, wrap the values with `I()`:
```{r}
ggheatmap(small_mat) +
    theme(
        axis.text.x = element_text(
            colour = I(c(rep("red", 4), rep("blue", 5)))
        ),
        axis.ticks.x = element_line(
            colour = I(c(rep("red", 4), rep("blue", 5)))
        ),
        axis.ticks.length.x = I(unit(rep(c(1, 4), times = c(4, 5)), "mm"))
    ) +
    hmanno("t") +
    align_dendro(aes(color = branch), k = 3L) +
    scale_y_continuous(expand = expansion()) &
    theme(plot.margin = margin())
```

## Facets
When working with facets, manual configuration of the panel using the
`facet_*()` functions is not possible since the internal structure will use
`facet_grid()` to set the row/column groups defined by `align_*()` functions.
However, you can still use `facet_grid()` or `facet_null()` (if no panel) to
control other arguments except layout axis panels (`rows` in horizontal stack
layout or `cols` in vertical stack layout, or both `rows` and `cols` in heatmap
body). 

A common use case is to modify the panel strip text. The default theme
(`theme_ggalign()`) will always remove the panel strip text, you can override
this behaviour with `theme(strip.text = element_text())` to add the panel title
in the plot area.
```{r}
ggheatmap(small_mat) +
    facet_grid(labeller = labeller(.column_panel = function(x) letters[as.integer(x)])) +
    theme(strip.text = element_text()) +
    hmanno("top") +
    align_kmeans(centers = 3L)
```

## Coords
Currently, only cartesian coordinate can be used to align axis well.
Internally, the limits will always be set to the number of observations, with an
additional range expansion of `0.5` added on both ends. 

## Session information
```{r}
sessionInfo()
```
