You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

213 lines
7.8KB

  1. #' Animation Options
  2. #'
  3. #' Helper function to set animation and plotting options to be passed to
  4. #' [animate_plot()] and [static_plot()].
  5. #'
  6. #' @param text_family Font family for the plot text, default is "Fira Mono". Use
  7. #' [set_font_size()] to set global default font sizes.
  8. #' @param title_family Font family for the plot title, default is "Fira Mono".
  9. #' Use [set_font_size()] to set global default font sizes.
  10. #' @param text_size Font size of the plot text, default is 5.
  11. #' @param title_size Font size of the plot title, default is 17.
  12. #' @param ease_default Default aes easing function. See [tweenr::display_ease()]
  13. #' for more options. The tidyexplain default value is `sine-in-out`.
  14. #' @param ease_other Additional aes easing options, specified as a named list.
  15. #' List entries are named with the aesthetic to which the easeing should be
  16. #' applied, consistent with [gganimate::ease_aes()]. E.g. `list(color =
  17. #' "sine")`.
  18. #' @param enter Enter fading function applied to objects in the animation. See
  19. #' [gganimate::enter_exit] for a complete list of options. The tidyexplain
  20. #' default is [gganimate::enter_fade()].
  21. #' @param exit Exit fading function applied to objects in the animation. See
  22. #' [gganimate::enter_exit] for a complete list of options. The tidyexplain
  23. #' default is [gganimate::exit_fade()].
  24. #' @inheritParams gganimate::transition_states
  25. #' @export
  26. anim_options <- function(
  27. transition_length = NULL,
  28. state_length = NULL,
  29. ease_default = NULL,
  30. ease_other = NULL,
  31. enter = NULL,
  32. exit = NULL,
  33. text_family = NULL,
  34. title_family = NULL,
  35. text_size = NULL,
  36. title_size = NULL,
  37. ...
  38. ){
  39. enter_name <- if (!missing(enter)) rlang::quo_name(rlang::enquo(enter))
  40. exit_name <- if (!missing(exit)) rlang::quo_name(rlang::enquo(exit))
  41. ao <- list(
  42. transition_length = transition_length,
  43. state_length = state_length,
  44. ease_default = ease_default,
  45. ease_other = ease_other,
  46. enter = if (!is.null(enter)) setNames(list(enter), enter_name),
  47. exit = if (!is.null(exit)) setNames(list(exit), exit_name),
  48. text_family = text_family,
  49. text_size = text_size,
  50. title_family = title_family,
  51. title_size = title_size,
  52. ...
  53. )
  54. ao <- purrr::compact(ao)
  55. structure(ao, class = "anim_opts")
  56. }
  57. # Global Animation Options Setters and Getters ----------------------------
  58. #' @describeIn anim_options Set default animation options for the current session.
  59. #' @param anim_opts An [anim_options()] options list.
  60. #' @export
  61. anim_options_set <- function(anim_opts = anim_options()) {
  62. stopifnot(is.anim_opts(anim_opts))
  63. ao_old <- plot_settings$anim_opts
  64. plot_settings$anim_opts <- merge(anim_opts, plot_settings$anim_opts)
  65. invisible(ao_old)
  66. }
  67. get_anim_opt <- function(anim_opt = NULL) {
  68. if (is.null(anim_opt)) return(plot_settings$anim_opts)
  69. if (anim_opt %in% c("text_size", "title_size")) rlang::abort(
  70. "Use get_text_size() or get_title_size()"
  71. )
  72. plot_settings$anim_opts[[anim_opt]] %||% plot_settings$default[[anim_opt]]
  73. }
  74. # Animation Options Methods -----------------------------------------------
  75. print.anim_opts <- function(ao, full = FALSE) {
  76. if (!full) ao <- purrr::discard(ao, is.null)
  77. # Replace ggproto (enter/exit functions) with their names
  78. if ("enter" %in% names(ao)) ao$enter <- paste("ggproto:", names(ao$enter))
  79. if ("exit" %in% names(ao)) ao$exit <- paste("ggproto:", names(ao$exit))
  80. x <- capture.output(str(ao, no.list = TRUE))
  81. cat(
  82. paste0("<anim_options: ", length(ao), " options>"),
  83. x, sep = "\n"
  84. )
  85. }
  86. is.anim_opts <- function(ao) inherits(ao, "anim_opts")
  87. # Fill, Validate, Merge Animation Options ---------------------------------
  88. # Fills in default animation options
  89. fill_anim_opts <- function(ao) {
  90. ao$transition_length <- ao$transition_length %||% get_anim_opt("transition_length")
  91. ao$state_length <- ao$state_length %||% get_anim_opt("state_length")
  92. ao$ease_default <- ao$ease_default %||% get_anim_opt("ease_default")
  93. ao$ease_other <- ao$ease_other %||% get_anim_opt("ease_other")
  94. ao$enter <- ao$enter %||% get_anim_opt("enter")
  95. ao$exit <- ao$exit %||% get_anim_opt("exit")
  96. ao$text_family <- ao$text_family %||% get_anim_opt("text_family")
  97. ao$title_family <- ao$title_family %||% get_anim_opt("title_family")
  98. ao
  99. }
  100. validate_anim_opts <- function(ao, quiet = FALSE, strict = getOption("tidyexplain.strict_dots", FALSE)) {
  101. if (!inherits(ao, "anim_opts")) {
  102. rlang::warn("Use `anim_options()` to set `anim_opts`")
  103. }
  104. ao <- fill_anim_opts(ao)
  105. stopifnot(is.ggproto(ao$enter[[1]]), is.ggproto(ao$exit[[1]]))
  106. extra_names <- setdiff(names(ao), names(formals(anim_options)))
  107. if (!quiet && length(extra_names)) {
  108. extra_names <- paste0(sprintf("`%s`", extra_names), collapse = ", ")
  109. msg <- paste("Unknown animation options will be ignored:", extra_names)
  110. if (isTrue(strict)) rlang::abort(msg) else rlang::warn(msg)
  111. }
  112. invisible(ao)
  113. }
  114. merge.anim_opts <- function(ao_new, ao_base = anim_options()) {
  115. ao_new <- purrr::discard(ao_new, is.null)
  116. ao_base <- purrr::discard(ao_base, is.null)
  117. unique_base <- setdiff(names(ao_base), names(ao_new))
  118. ao <- append(ao_new, ao_base[unique_base])
  119. ao <- ao[names(formals(anim_options))]
  120. ao <- purrr::discard(ao, is.null)
  121. class(ao) <- "anim_opts"
  122. ao
  123. }
  124. # Default Animation Options for Verb Families -----------------------------
  125. default_anim_opts <- function(family, ao_custom = NULL) {
  126. family_options <- c("join", "set", "gather", "spread")
  127. family <- match.arg(family, family_options, several.ok = FALSE)
  128. ao_default <- switch(
  129. family,
  130. "gather" = anim_options(enter = enter_fade(), exit = exit_fade(),
  131. ease_default = "sine-in-out",
  132. ease_other = list(y = "cubic-out", x = "cubic-in")),
  133. "spread" = anim_options(enter = enter_fade(), exit = exit_fade(),
  134. ease_default = "sine-in-out",
  135. ease_other = list(y = "cubic-out", x = "cubic-in")),
  136. anim_options()
  137. )
  138. if (is.null(ao_custom)) {
  139. # User set globals override defaults
  140. ao_custom <- get_anim_opt()
  141. } else {
  142. # Opts from function call override user-set globals
  143. ao_custom <- merge(ao_custom, get_anim_opt())
  144. }
  145. # function > user-set global > default (> global default)
  146. if (!is.null(ao_custom)) merge(ao_custom, ao_default) else ao_default
  147. }
  148. # Font Size Setters and Getters -------------------------------------------
  149. #' Set Default Text Sizes for Animation Plots
  150. #'
  151. #' Sets the default text sizes for the animated and static plots produced by
  152. #' this package during the current session.
  153. #'
  154. #' @param text_size Font size of value labels inside the data frame squares
  155. #' @param title_size Font size of the function call or plot title
  156. #' @export
  157. set_font_size <- function(text_size = NULL, title_size = NULL) {
  158. old <- list()
  159. if (!is.null(text_size)) old$text_size <- set_text_size(text_size)
  160. if (!is.null(title_size)) old$title_size <- set_title_size(title_size)
  161. invisible(old)
  162. }
  163. #' @describeIn set_font_size Get current global font sizes
  164. #' @export
  165. get_font_size <- function() {
  166. list("text_size" = get_text_size(), "title_size" = get_title_size())
  167. }
  168. set_text_size <- function(size) {
  169. old <- plot_settings$text_size
  170. anim_options_set(anim_options(text_size = size))
  171. invisible(old)
  172. }
  173. set_title_size <- function(size) {
  174. old <- plot_settings$title_size
  175. anim_options_set(anim_options(title_size = size))
  176. invisible(old)
  177. }
  178. get_text_size <- function(x = NULL) {
  179. if (!is.null(x)) return(x)
  180. plot_settings$anim_opts$text_size %||%
  181. getFromNamespace("theme_env", "ggplot2")$current$text$size %||%
  182. plot_settings$default$text_size
  183. }
  184. get_title_size <- function(x = NULL) {
  185. if (!is.null(x)) return(x)
  186. plot_settings$anim_opts$title_size %||%
  187. getFromNamespace("theme_env", "ggplot2")$current$plot.title$size %||%
  188. plot_settings$default$title_size
  189. }