3

Knitr has a rather neat animation feature which can produce animations in Markdown and HTML via FFMPEG and WebM, and in PDF using the animate LaTeX package. However, support for PDFs for the later is limited to Adobe Reader (and possibly PDF XChangeViewer). I'm thinking of implementing my own animation functionality which would produce an MP4 video from a series of plots and use the LaTeX package media9 to embed the video. This has wider support, including by Okular and at least one iOS PDF reader. Is there another way to do this that's already out there either within knitr or in another package? It seems it would have general applicability if implemented well.

Note I've posted a worked example of animation using rgl as an answer to this question. It works fine in Adobe Reader but relies on scripting support.

Community
  • 1
  • 1
Nick Kennedy
  • 12,510
  • 2
  • 30
  • 52

1 Answers1

1

I've got something working that converts a series of pngs to mp4 and then embeds it using the media9 package. It plays back fine in Adobe Reader on Windows, but for some reason doesn't work on Okular running on Linux Mate. Having said that, nor do any other pdfs I've tried with embedded movies, so it seems to be an issue on the reader side.

Here's the .Rnw:

\documentclass{article}
\usepackage{media9}
\usepackage{graphicx, xcolor}

<< label = setup, include = FALSE>>=
library("rgl")
library("car")
library("knitr")
hook_rgl_spin_mp4 <- function(before, options, envir) {
  if (!before) {
    par3d(windowRect = 100 + options$dpi *
          c(0, 0, options$fig.width, 
            options$fig.height))
    if (!is.null(options$spin3d.axis)) {
      spin3d.axis <- options$spin3d.axis
    } else {
      spin3d.axis <- c(0, 0, 1)
    }
    if (!is.null(options$spin3d.rpm)) {
      spin3d.rpm <- options$spin3d.rpm
    } else {
      spin3d.rpm <- c(0, 0, 1)
    }
    spinFunc <- spin3d(axis = spin3d.axis, rpm = spin3d.rpm)
    for(i in 1:options$fig.num) {
      par3d(spinFunc(i * options$interval))
      Sys.sleep(0.05)
      rgl.snapshot(fig_path(".png", number = i), fmt = "png")
    }
    system(paste0('ffmpeg -y -r 10 -i "', sub("-1\\.", "-%d\\.", fig_path(".png", number = 1)),
                  '" -pix_fmt yuv420p "', vid_path <- fig_path(".mp4", number = 1), '"'))
    options$fig.num <- 0
    options$fig.show <- "hide"
    paste0("\\includemedia[width=", options$out.width, ",height=", options$out.height, 
           ",activate=pageopen,addresource=",
           vid_path, ",flashvars={source=", vid_path, "}]{}{VPlayer.swf}")
  }
}
knit_hooks$set(rgl = hook_rgl_spin_mp4)
@ 
  \begin{document}

<< label=rgl1, rgl=TRUE, fig.show='animate', fig.width=5, fig.height=5, out.width='.6\\linewidth', out.height='.6\\linewidth', dev='png', fig.num = 40, interval=0.1, spin3d.axis=c(0, 0, 1), spin3d.rpm=20, results='asis'>>=
  scatter3d(prestige ~ income + education, data=Duncan)
@

  \end{document}

Note it has to have the results='asis' to work, but otherwise everything is done in the hook.

Nick Kennedy
  • 12,510
  • 2
  • 30
  • 52