File: saveLatex.R

package info (click to toggle)
r-cran-animation 2.7%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, forky, sid, trixie
  • size: 1,268 kB
  • sloc: javascript: 873; sh: 15; makefile: 2
file content (215 lines) | stat: -rw-r--r-- 10,244 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
#' Insert animations into a LaTeX document and compile it
#'
#' Record animation frames and insert them into a LaTeX document with the
#' \code{animate} package. Compile the document if an appropriate LaTeX command
#' is provided.
#'
#' This is actually a wrapper to generate a LaTeX document using R. The document
#' uses the LaTeX package called \code{animate} to insert animations into PDF's.
#' When we pass an R expression to this function, the expression will be
#' evaluated and recorded by a grahpics device (typically \code{\link{png}} and
#' \code{\link{pdf}}). At last, a LaTeX document will be created and compiled if
#' an appropriate LaTeX command is provided. And the final PDF output will be
#' opened with the PDF viewer set in \code{getOption('pdfviewer')} if
#' \code{ani.options('autobrowse') == TRUE}.
#' @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 nmax maximum number of animation frames (if missing and the graphics
#'   device is a bitmap device, this number will be automatically calculated);
#'   note that we do not have to specify \code{nmax} when using PDF devices.
#' @param img.name basename of file names of animation frames; see the Note
#'   section for a possible adjustment on \code{img.name}
#' @param ani.opts options to control the behavior of the animation (passed to
#'   the LaTeX macro \code{'\\animategraphics'}; default to be
#'   \code{'controls,width=\\linewidth'})
#' @param centering logical: whether to center the graph using the LaTeX
#'   environment \verb{\begin{center}} and \verb{\end{center}}
#' @param caption,label caption and label for the graphics in the figure
#'   environment
#' @param pkg.opts global options for the \code{animate} package
#' @param documentclass LaTeX document class; if \code{NULL}, the output will
#'   not be a complete LaTeX document (only the code to generate the PDF
#'   animation will be printed in the console); default to be \code{article},
#'   but we can also provide a complete statement like
#'   \verb{\\documentclass[a5paper]{article}}
#' @param latex.filename file name of the LaTeX document; if an empty string
#'   \code{''}, the LaTeX code will be printed in the console and hence not
#'   compiled
#' @param pdflatex the command for pdfLaTeX (set to \code{NULL} to ignore the
#'   compiling)
#' @param install.animate copy the LaTeX style files \file{animate.sty} and
#'   \file{animfp.sty}? If you have not installed the LaTeX package
#'   \code{animate}, it suffices just to copy these to files.
#' @param overwrite whether to overwrite the existing image frames
#' @param full.path whether to use the full path (\code{TRUE}) or relative path
#'   (\code{FALSE}) for the animation frames; usually the relative path
#'   suffices, but sometimes the images and the LaTeX document might not be in
#'   the same directory, so \code{full.path = TRUE} could be useful; in the
#'   latter case, remember that you should never use spaces in the filenames or
#'   paths!
#' @param ... other arguments passed to the graphics device
#'   \code{ani.options('ani.dev')}, e.g. \code{ani.height} and \code{ani.width}
#'
#' @return Invisible \code{NULL}
#' @note This function will detect if it was called in a Sweave environment --
#'   if so, \code{img.name} will be automatically adjusted to
#'   \code{prefix.string-label}, and the LaTeX output will not be a complete
#'   document, but rather a single line like
#'   \preformatted{\animategraphics[ani.opts]{1/interval}{img.name}{}{}}
#'
#'   This automatic feature can be useful to Sweave users (but remember to set
#'   the Sweave option \code{results=tex}). See \code{demo('Sweave_animation')}
#'   for a complete example.
#'
#'   PDF devices are recommended because of their high quality and usually they
#'   are more friendly to LaTeX, but the size of PDF files is often much larger;
#'   in this case, we may set the option \code{'qpdf'} or \code{'pdftk'} to
#'   compress the PDF graphics output. To set the PDF device, use
#'   \code{ani.options(ani.dev = 'pdf', ani.type = 'pdf')}
#'
#'   So far animations created by the LaTeX package \pkg{animate} can only be
#'   viewed with Acrobat Reader (Windows) or \command{acroread} (Linux).  Other
#'   PDF viewers may not support JavaScript (in fact the PDF animation is driven
#'   by JavaScript). Linux users may need to install \command{acroread} and set
#'   \code{options(pdfviewer = 'acroread')}.
#' @author Yihui Xie
#' @family utilities
#' @references Examples at \url{https://yihui.org/animation/example/savelatex/}
#'
#'   To know more about the \code{animate} package, please refer to
#'   \url{http://www.ctan.org/tex-archive/macros/latex/contrib/animate/}. There
#'   are a lot of options can be set in \code{ani.opts} and \code{pkg.opts}.
#' @export
saveLatex = function(
  expr, nmax, img.name = 'Rplot', ani.opts, centering = TRUE, caption = NULL,
  label = NULL, pkg.opts = NULL, documentclass = 'article', latex.filename = 'animation.tex',
  pdflatex = 'pdflatex', install.animate = TRUE, overwrite = TRUE, full.path = FALSE, ...
) {
  oopt = ani.options(...)
  if (!missing(nmax)) ani.options(nmax = nmax)
  on.exit(ani.options(oopt))
  file.ext = ani.options('ani.type')
  use.dev = ani.options('use.dev')
  ## detect if I'm in a Sweave environment
  in.sweave = FALSE
  if ((n.parents <- length(sys.parents())) >= 3) {
    for (i in seq_len(n.parents) - 1) {
      if ('chunkopts' %in% ls(envir = sys.frame(i))) {
        chunkopts = get('chunkopts', envir = sys.frame(i))
        if (all(c('prefix.string', 'label') %in% names(chunkopts))) {
          ## yes, I'm in Sweave w.p. 95%
          img.name = paste(chunkopts$prefix.string, chunkopts$label, sep = '-')
          ani.options(img.fmt = paste(img.name, ani.options('imgnfmt'), '.', file.ext, sep = ''))
          in.sweave = TRUE
          break
        }
      }
    }
  }

  interval = ani.options('interval')
  ## generate the image frames
  ani.dev = ani.options('ani.dev')
  num = ifelse(file.ext == 'pdf' && use.dev, '', ani.options('imgnfmt'))
  img.fmt = sprintf('%s%s.%s', img.name, num, file.ext)
  if (!in.sweave)
    ani.options(img.fmt = img.fmt)
  if (is.character(ani.dev))
    ani.dev = get(ani.dev)
  ani.files.len = length(
    list.files(path = dirname(img.name), pattern =
                 sprintf('^%s[0-9]*\\.%s$', basename(img.name), file.ext))
  )
  if (overwrite || !ani.files.len) {
    if (use.dev)
      ani.dev(img.fmt, width = ani.options('ani.width'), height = ani.options('ani.height'))
    expr
    if (use.dev) dev.off()
    ## compress PDF files
    if (file.ext == 'pdf') compress_pdf(img.name)
  }
  ani.files.len = length(
    list.files(path = dirname(img.name), pattern = sprintf('^%s[0-9]*\\.%s$', basename(img.name), file.ext))
  )

  if (missing(nmax)) {
    ## count the number of images generated
    start.num = ifelse(file.ext == 'pdf' && use.dev, '', 1)
    end.num = ifelse(file.ext == 'pdf' && use.dev, '', ani.files.len)
  } else {
    ## PDF animations should start from 0 to nmax-1
    start.num = ifelse(file.ext == 'pdf' && use.dev, 0, 1)
    end.num = ifelse(file.ext == 'pdf' && use.dev, nmax - 1, nmax)
  }

  if (missing(ani.opts)) ani.opts = 'controls,width=\\linewidth'

  if (install.animate && !in.sweave && length(documentclass)) {
    file.copy(system.file('misc', 'animate', 'animate.sty', package = 'animation'),
              'animate.sty', overwrite = TRUE)
    file.copy(system.file('misc', 'animate', 'animfp.sty', package = 'animation'),
              'animfp.sty', overwrite = TRUE)
  }

  if (!in.sweave && length(documentclass)) {
    if (img.name == sub('\\.tex$', '', latex.filename))
      stop("'img.name' should not be the same with 'latex.filename'!")
    if (!grepl('^\\\\documentclass', documentclass))
      documentclass = sprintf('\\documentclass{%s}', documentclass)
    cat(sprintf(
      '%s
                \\usepackage%s{animate}
                \\begin{document}
                \\begin{figure}
                %s
                \\animategraphics[%s]{%s}{%s}{%s}{%s}%s%s
                %s
                \\end{figure}
                \\end{document}
                ', documentclass,
      ifelse(is.null(pkg.opts), '', sprintf('[%s]', pkg.opts)),
      ifelse(centering, '\\begin{center}', ''),
      ani.opts,
      1/interval,
      ifelse(full.path, gsub('\\\\', '/', normalizePath(img.name)), img.name),
      start.num, end.num,
      ifelse(is.null(caption), '', sprintf('\\caption{%s}', caption)),
      ifelse(is.null(label), '', sprintf('\\label{%s}', label)),
      ifelse(centering, '\\end{center}', '')),
        '\n', file = latex.filename
    )
    if ((latex.filename != '') & !is.null(pdflatex)) {
      message('LaTeX document created at: ', file.path(getwd(), latex.filename))
      if (system(sprintf('%s %s', pdflatex, latex.filename)) == 0) {
        message(sprintf('successfully compiled: %s %s', pdflatex, latex.filename))
        if (ani.options('autobrowse'))
          system(
            sprintf('%s %s', shQuote(normalizePath(getOption('pdfviewer'))),
                    sprintf('%s.pdf', sub('([^.]+)\\.[[:alnum:]]+$', '\\1', latex.filename)))
          )
      }
      else {
        message('An error occurred while compiling the LaTeX document; \nyou should probably take a look at the log file: ',
                sprintf('%s.log', sub('([^.]+)\\.[[:alnum:]]+$',
                                      '\\1', latex.filename)), ' under ', getwd())
        if (Sys.info()['sysname'] == 'Darwin')
          message("Mac OS users may also consider saveLatex(..., pdflatex = '/usr/texbin/pdflatex') if pdflatex is not in your PATH variable.")
      }
    }
  } else {
    cat(sprintf(
      '%s
                \\animategraphics[%s]{%s}{%s}{%s}{%s}
                %s
                ',
      ifelse(centering, '\\begin{center}', ''),
      ani.opts,
      1/interval,
      ifelse(full.path, gsub('\\\\', '/', normalizePath(img.name)), img.name),
      start.num, end.num,
      ifelse(centering, '\\end{center}', ''))
    )
  }
  invisible(NULL)
}