R/saveGIF.R

Defines functions saveGIF

Documented in saveGIF

#' Convert images to a single animation file (typically GIF) using ImageMagick
#' or GraphicsMagick
#'
#' This function opens a graphical device (specified in
#' \code{ani.options('ani.dev')}) first to generate a sequence of images based
#' on \code{expr}, then makes use of the command \command{convert} in
#' `ImageMagick' to convert these images to a single animated movie (as a GIF or
#' MPG file). An alternative software package is GraphicsMagick (use
#' \code{convert = 'gm convert'}), which is smaller than ImageMagick.
#'
#' This function calls \code{\link{im.convert}} (or \code{\link{gm.convert}},
#' depending on the argument \code{convert}) to convert images to a single
#' animation.
#'
#' The advantage of this function is that it can create a single movie file,
#' however, there are two problems too: (1) we need a special (free) software
#' ImageMagick or GraphicsMagick; (2) the speed of the animation will be beyond
#' our control, as the \code{interval} option is fixed. Other approaches in this
#' package may have greater flexibilities, e.g. the HTML approach (see
#' \code{\link{saveHTML}}).
#'
#' See \code{\link{ani.options}} for the options that may affect the output,
#' e.g.  the graphics device (including the height/width specifications), the
#' file extension of image frames, and the time interval between image frames,
#' etc.  Note that \code{ani.options('interval')} can be a numeric vector!
#' @param expr an expression to generate animations; use either the animation
#'   functions (e.g. \code{brownian.motion()}) in this package or a custom
#'   expression (e.g. \code{for(i in 1:10) plot(runif(10), ylim = 0:1)}).
#' @param movie.name file name of the movie (with the extension)
#' @param img.name file name of the sequence of images (`pure' name; without any
#'   format or extension)
#' @param convert the command to convert images (default to be \command{convert}
#'   (i.e. use ImageMagick), but might be \command{imconvert} under some Windows
#'   platforms); can be \command{gm convert} in order to use GraphicsMagick; see
#'   the 'Note' section for details
#' @param cmd.fun a function to invoke the OS command; by default
#'   \code{\link{system}}
#' @param clean whether to delete the individual image frames
#' @param extra.opts additional options passed to \code{\link{im.convert}}
#' @param \dots other arguments passed to \code{\link{ani.options}}, e.g.
#'   \code{ani.height} and \code{ani.width}, ...
#' @return The command for the conversion (see \code{\link{im.convert}}).
#' @note See \code{\link{im.convert}} for details on the configuration of
#'   ImageMagick (typically for Windows users) or GraphicsMagick.
#'
#'   It is recommended to use \code{ani.pause()} to pause between animation
#'   frames in \code{expr}, because this function will only pause when called in
#'   a non-interactive graphics device, which can save a lot of time.  See the
#'   demo \code{'Xmas2'} for example (\code{demo('Xmas2', package =
#'   'animation')}).
#'
#'   \code{\link{saveGIF}} has an alias \code{\link{saveMovie}} (i.e. they are
#'   identical); the latter name is for compatibility to older versions of this
#'   package (< 2.0-2). It is recommended to use \code{\link{saveGIF}} to avoid
#'   confusions between \code{\link{saveMovie}} and \code{\link{saveVideo}}.
#' @author Yihui Xie
#' @family utilities
#' @references Examples at \url{https://yihui.org/animation/example/savegif/}
#'
#'   ImageMagick: \url{http://www.imagemagick.org/script/convert.php};
#'
#'   GraphicsMagick: \url{http://www.graphicsmagick.org}
#' @export
saveGIF = function(
  expr, movie.name = 'animation.gif', img.name = 'Rplot', convert = 'magick',
  cmd.fun, clean = TRUE, extra.opts = "", ...
) {
  oopt = ani.options(...)
  on.exit(ani.options(oopt))
  if(!dir.exists(dirname(movie.name))){
    dir.create(dirname(movie.name))
  }
  ## create images in the temp dir
  owd = setwd(tempdir())
  on.exit(setwd(owd), add = TRUE)

  file.ext = ani.options('ani.type')

  ## clean up the files first
  unlink(paste(img.name, '*.', file.ext, sep = ''))

  ## draw the plots and record them in image files
  ani.dev = ani.options('ani.dev')
  if (is.character(ani.dev)) ani.dev = get(ani.dev)
  img.fmt = paste(img.name, ani.options('imgnfmt'), '.', file.ext, sep = '')

  if ((use.dev <- ani.options('use.dev'))){
    if ("res" %in% names(formals(ani.dev))){
      ani.dev(file.path(tempdir(), img.fmt), width = ani.options('ani.width'),
              height = ani.options('ani.height'), res = ani.options('ani.res'))
    } else {
      ani.dev(file.path(tempdir(), img.fmt), width = ani.options('ani.width'),
              height = ani.options('ani.height'))
    }
  }
  in_dir(owd, expr)
  if (use.dev) dev.off()

  ## compress PDF files
  if (file.ext == 'pdf')
    compress_pdf(img.name)
  img.files = sprintf(img.fmt, seq_len(length(list.files(
    pattern = paste(img.name, '[0-9]+\\.', file.ext, sep = '')
  ))))
  if (missing(cmd.fun))
    cmd.fun = if (.Platform$OS.type == 'windows') shell else system
  ## convert to animations
  im.convert(img.files, output = path.expand(movie.name), convert = convert,
             cmd.fun = cmd.fun, clean = clean, extra.opts = extra.opts)
  setwd(owd)
  if (!grepl(tempdir(),movie.name,fixed = T)){
    file.copy(file.path(tempdir(), basename(movie.name)),
              path.expand(movie.name), overwrite = TRUE)
    # auto_browse(path.expand(movie.name))
  }
}
#' @rdname saveGIF
saveMovie = saveGIF
yihui/animation documentation built on March 27, 2023, 2:50 p.m.