Skip to content

Commit

Permalink
remove the gganno2
Browse files Browse the repository at this point in the history
  • Loading branch information
Yunuuuu committed Jun 20, 2024
1 parent f234120 commit aa7a967
Show file tree
Hide file tree
Showing 20 changed files with 573 additions and 403 deletions.
2 changes: 1 addition & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ Suggests:
rmarkdown
Collate:
'anno-.R'
'anno-fn.R'
'anno-subsettable-.R'
'eheat-package.R'
'ggheat.R'
'gganno.R'
Expand Down
4 changes: 3 additions & 1 deletion NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@

export(draw)
export(gganno)
export(gganno2)
export(ggheat)
export(gpar)
export(ht_opt)
export(new_anno)
export(new_anno_subset)
export(unit)
exportClasses(ggAnnotationFunction)
exportClasses(ggHeatmap)
Expand All @@ -15,6 +16,7 @@ importClassesFrom(ComplexHeatmap,AnnotationFunction)
importClassesFrom(ComplexHeatmap,Heatmap)
importFrom(ComplexHeatmap,draw)
importFrom(ComplexHeatmap,ht_opt)
importFrom(ComplexHeatmap,make_layout)
importFrom(ComplexHeatmap,prepare)
importFrom(ggplot2,aes)
importFrom(ggplot2,ggplot)
Expand Down
59 changes: 55 additions & 4 deletions R/anno-.R
Original file line number Diff line number Diff line change
@@ -1,13 +1,65 @@
#' @inherit ComplexHeatmap::AnnotationFunction
#' @param draw_fn A function which defines how to draw the annotation. See
#' [ComplexHeatmap
#' Manual](https://jokergoo.github.io/ComplexHeatmap-reference/book/heatmap-annotations.html#implement-new-annotation-functions)
#' for details.
#'
#' The function should have three arguments: `index`, `k` and `n` (the names of
#' the arguments can be arbitrary) where `k` and `n` are optional. `index`
#' corresponds to the indices of rows or columns of the heatmap. The value of
#' `index` is not necessarily to be the whole row indices or column indices in
#' the heatmap. It can also be a subset of the indices if the annotation is
#' split into slices according to the split of the heatmap. `index` is
#' reordered according to the reordering of heatmap rows or columns (e.g. by
#' clustering). So, `index` actually contains a list of row or column indices
#' for the current slice after row or column reordering.
#'
#' k corresponds to the current slice and n corresponds to the total number of
#' slices.
#'
#' @param ylim Ranges of data value on the data axis.
#' @param name Name of the annotation, only used for message.
#' @details
#' `new_anno` is similar with
#' [AnnotationFunction][ComplexHeatmap::AnnotationFunction], but `new_anno`
#' won't change the function environment of `draw_fn`. So it's safe to use
#' `new_anno` in pacakge development, particularly when dealing with internal
#' functions in the package namespace that are likely to exist. `@@subsettable`
#' will always be set to `FALSE`.
#' @examples
#' x <- 1:10
#' anno1 <- new_anno(
#' n = 10,
#' draw_fn = function(index, k, n) {
#' n <- length(index)
#' pushViewport(viewport(xscale = c(0.5, n + 0.5), yscale = c(0, 10)))
#' grid.rect()
#' grid.points(1:n, x[index], default.units = "native")
#' if (k == 1) grid.yaxis()
#' popViewport()
#' },
#' height = unit(2, "cm")
#' )
#' m <- rbind(1:10, 11:20)
#' ggheat(m, top_annotation = HeatmapAnnotation(foo = anno1))
#' ggheat(m, top_annotation = HeatmapAnnotation(foo = anno1), column_km = 2)
#' @seealso
#' - [new_anno_subset]
#' - [AnnotationFunction][ComplexHeatmap::AnnotationFunction]
#' @export
new_anno <- function(n, draw_fn, ylim = NULL,
subset_rule = list(), subsettable = TRUE,
width = NULL, height = NULL, show_name = TRUE,
which = NULL, name = NULL) {
# ComplexHeatmap::AnnotationFunction() will change the function
# environment of `anno@fun`
# here: we use new_anno instead, in this way, the function in the
# package namespace can be used directly
assert_string(name, null_ok = TRUE)
name <- name %||% ""
if (ht_opt$verbose) {
cli::cli_inform("construct AnnotationFunction with {.fn {name}}")
msg <- "construct AnnotationFunction"
if (name == "") msg <- paste(msg, "with {.fn {name}}")
cli::cli_inform(msg)
}
anno <- methods::new("AnnotationFunction")
which <- cheat_which(which)
Expand All @@ -21,8 +73,7 @@ new_anno <- function(n, draw_fn, ylim = NULL,
anno@data_scale <- ylim %||% c(0L, 1L)
anno@fun <- draw_fn
anno@var_env <- new.env(parent = environment(draw_fn))
anno@subsettable <- subsettable
anno@subset_rule <- subset_rule
anno@subsettable <- FALSE
anno
}

Expand Down
118 changes: 0 additions & 118 deletions R/anno-fn.R

This file was deleted.

154 changes: 154 additions & 0 deletions R/anno-subsettable-.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
#' Subsettable AnnotationFunction
#'
#' @inheritParams ggheat
#' @param draw_fn A function which defines how to draw the annotation. The
#' function should accept at least `matrix`, `index`, `k`, `n` arguments.
#' `...` is also passed on to this function.
#' @param ... Additional arguments passed on to `draw_fn`.
#' @inheritParams new_anno
#' @inheritParams ComplexHeatmap::AnnotationFunction
#' @param subset_rule A list of function to subset variables in `...`.
#' @details
#' `new_anno_subset` is similar with
#' [AnnotationFunction][ComplexHeatmap::AnnotationFunction], but
#' `new_anno_subset` won't change the function environment of `draw_fn`. So it's
#' safe to use `new_anno_subset` in pacakge development, particularly when
#' dealing with internal functions in the package namespace that are likely to
#' exist. You must always ensure arguments passed on to `...` have subset rules
#' since the internal will set `@@subsettable` to `TRUE`.
#' @examples
#' anno <- new_anno_subset(
#' rnorm(10L),
#' draw_fn = function(matrix, index, k, n) {
#' n <- length(index)
#' pushViewport(viewport(xscale = c(0.5, n + 0.5), yscale = c(0, 10)))
#' grid.rect()
#' grid.points(1:n,
#' matrix[index, , drop = FALSE],
#' default.units = "native"
#' )
#' if (k == 1) grid.yaxis()
#' popViewport()
#' },
#' height = unit(2, "cm")
#' )
#' draw(anno)
#' @seealso
#' - [new_anno]
#' - [AnnotationFunction][ComplexHeatmap::AnnotationFunction]
#' @inherit ComplexHeatmap::AnnotationFunction return
#' @export
new_anno_subset <- function(
matrix, draw_fn, ..., ylim = NULL, subset_rule = NULL,
width = NULL, height = NULL, show_name = TRUE,
which = NULL, name = NULL) {
matrix <- build_matrix(matrix)
draw_fn <- allow_lambda(draw_fn)
if (...length() != sum(nzchar(...names()))) {
cli::cli_abort("All elements in {.arg ...} must be named.")
}
name <- name %||% "anno_subset"
dots <- rlang::list2(...)
# https://github.com/jokergoo/ComplexHeatmap/blob/7d95ca5cf533b98bd0351eecfc6805ad30c754c0/R/AnnotationFunction-class.R#L270
internal_subset <- list(matrix = function(x, i) x[i, , drop = FALSE])
if (...length() && is.null(subset_rule)) {
subset_rule <- lapply(dots, function(var) {
if (is.matrix(var)) {
function(x, i) x[i, , drop = FALSE]
} else if (inherits(var, "gpar")) {
subset_gp
} else if (is.vector(var)) {
if (length(var) > 1) function(x, i) x[i]
}
})
}
if (length(subset_rule)) {
rules <- subset_rule
rules_nms <- names(rules)
subset_rule <- list(dots = function(x, i) {
imap(x, function(element, nm) {
if (any(nm == rules_nms)) {
rule <- rules[[nm]]
if (is.null(rule) || isFALSE(rule)) {
# Don't do subset
element
} else {
# subset element
rule(element, i)
}
} else {
element
}
})
})
}
subset_rule <- c(internal_subset, subset_rule)
which <- cheat_which(which)
anno <- new_anno(
n = nrow(matrix),
draw_fn = function(index, k, n) {
rlang::inject(draw_fn(
matrix = matrix, !!!dots,
index = index, k = k, n = n
))
},
ylim = ylim,
which = which, width = width, height = height,
show_name = show_name, name = name
)
anno@subsettable <- TRUE
anno@subset_rule <- subset_rule
# we change `var_env` into the environment of `draw_fn`
anno@var_env <- environment(anno@fun)
# Only save necessary variables for usage of `draw_fn`
on.exit(rm(
list = setdiff(
ls(envir = anno@var_env, all.names = TRUE),
c("draw_fn", "matrix", "dots")
),
envir = anno@var_env, inherits = FALSE
))
anno
}

anno_check_matrix <- function(matrix, which, heat_matrix, name) {
if (is.null(matrix) && is.null(heat_matrix)) {
cli::cli_abort("{.arg matrix} must be provided")
} else if (is.null(matrix)) {
matrix <- heat_matrix
} else {
if (is.function(matrix)) {
matrix <- matrix(heat_matrix)
}
matrix <- build_matrix(matrix)
if (!is.null(heat_matrix)) {
# check heat_matrix and anno_matrix are compatible
bad_matrix <- switch(which,
row = nrow(matrix) == nrow(heat_matrix),
column = nrow(matrix) == ncol(heat_matrix)
)
if (bad_matrix) {
msg <- sprintf("(%s) annotation matrix", style_fn(name))
msg <- paste(msg, "is not compatible with heatmap matrix",
sep = " "
)
cli::cli_abort(msg)
}
}
}
matrix
}

scale_get_limits <- function(matrix, scale = NULL) {
if (is.null(scale)) {
if (is_discrete(matrix)) {
scale <- ggplot2::scale_y_discrete()
} else {
scale <- ggplot2::scale_y_continuous()
}
}
new_scale <- scale$clone()
new_scale$reset()
new_scale$train(matrix)
new_scale$get_limits()
}
Loading

0 comments on commit aa7a967

Please sign in to comment.