Animated rgl graphs with knitr

RAnimationKnitrRglRnw

R Problem Overview


I want to include animated rgl graphs in my .Rnw document through knitr. Here is my MWE:

\documentclass{article}

<< label = setup, include = FALSE>>=
opts_chunk$set(fig.path = 'figure/',  cache = FALSE, dev = "pdf",  fig.align = 'center', fig.show = 'hold', fig.width = 3, fig.height = 3,  echo = TRUE, warning = FALSE, message = FALSE, size = 'footnotesize', comment=NA, results='hold')

knit_hooks$set(par = function(before, options, envir){
if (before && options$fig.show!='none')
 par(mar = c(4, 4, 0.1, 0.1), cex.lab = 0.95, cex.axis = 0.9, mgp = c(2, 0.7, 0), tcl = -0.3)
}
)
knit_hooks$set(rgl = function(before, options, envir) {
  if (!before) {
    ## after a chunk has been evaluated
    if (rgl.cur() == 0) return()  # no active device
    name = paste(options$fig.path, options$label, sep = '')
    rgl.snapshot(paste(name, '.png', sep = ''), fmt = 'png')
    return(paste('\\includegraphics{', name, '}\n', sep = ''))
  }
}
)

options(replace.assign = TRUE, width = 60)
@ 
\begin{document}
 
<< label=packages >>=
library(car)
@
<< label=rgl1, rgl=TRUE, fig.show='animate' >>=
scatter3d(prestige ~ income + education, data=Duncan)
@
  
\end{document}

I'm not getting graph in my knitted documents.

Updated

I'm still unable to get it work and getting the following warning:

Warning messages:
1: In rgl.snapshot(paste(name, ".png", sep = ""), fmt = "png") :
  RGL: Pixmap save: unable to open file 'D:\A B\C D UAF\Test\knitr\rglAnimation\figure\rgl1.png' for writing
2: In rgl.snapshot(paste(name, ".png", sep = ""), fmt = "png") :
  snapshot failed
3: running command '"C:\PROGRA~2\MIKTEX~1.9\miktex\bin\x64\texi2dvi.exe" --quiet --pdf "rglAnimation.tex" --max-iterations=20 -I "C:/PROGRA~1/R/R-31~1.1/share/texmf/tex/latex" -I "C:/PROGRA~1/R/R-31~1.1/share/texmf/bibtex/bst"' had status 1 

My sessionInfo() is

R version 3.1.1 (2014-07-10)
Platform: x86_64-w64-mingw32/x64 (64-bit)

locale:
[1] LC_COLLATE=English_United States.1252 
[2] LC_CTYPE=English_United States.1252   
[3] LC_MONETARY=English_United States.1252
[4] LC_NUMERIC=C                          
[5] LC_TIME=English_United States.1252    

attached base packages:
[1] tools     stats     graphics  grDevices utils     datasets  methods  
[8] base     

other attached packages:
[1] mgcv_1.8-2    nlme_3.1-117  rgl_0.93.1098 car_2.0-21    knitr_1.6.15 

loaded via a namespace (and not attached):
[1] evaluate_0.5.5  formatR_1.0     grid_3.1.1      highr_0.3      
[5] lattice_0.20-29 MASS_7.3-34     Matrix_1.1-4    nnet_7.3-8     
[9] stringr_0.6.2  

Edited

Changing fig.path = 'figure/' to fig.path = '' in opts_chunk$set compiles the document with png graphs but without any animation. How can I get animated rgl graphs with fig.path = 'figure/'.

R Solutions


Solution 1 - R

I've got an example based on yours that works using knitr on Windows 8.1 within RStudio 0.99.441.

This produces a 40-frame animation of the plot. It uses the built-in hook_plot_custom to include the plots which are generated manually by animating the 3d plot. The code for the animation was based on those in the help and source of play3d and movie3d. movied3d itself cannot be used because it is too inflexible in its file naming.

I've put this up on github at https://github.com/NikNakk/testAnimateRgl/ . The pdf is at https://github.com/NikNakk/testAnimateRgl/raw/master/rglKnitr.pdf

\documentclass{article}
\usepackage{animate}

<< label = setup, include = FALSE>>=
library("rgl")
library("car")
library("knitr")
knit_hooks$set(rgl = hook_plot_custom)
@ 
\begin{document}

<< label=rgl1, rgl=TRUE, fig.show='animate', fig.width=5, fig.height=5, out.width='.6\\linewidth', dev='png', fig.num = 40, interval=0.1>>=
scatter3d(prestige ~ income + education, data=Duncan)
M <- par3d("userMatrix")
par3d(windowRect = 100 + opts_current$get("dpi") *
        c(0, 0, opts_current$get("fig.width"), 
        opts_current$get("fig.height")))
spinFunc <- par3dinterp(userMatrix=list(M,
                             rotate3d(M, pi/2, 1, 0, 0),
                             rotate3d(M, pi/2, 0, 1, 0)))
for(i in 1:40) {
  par3d(spinFunc(i / 10))
  Sys.sleep(0.05)
  rgl.snapshot(fig_path(".png", number = i), fmt = "png")
}
@

\end{document}

Edit: New version

Here's another version which demonstrates the use of custom chunk options to set the parameters for the rather simpler spin3d. Note that with this version, the chunk is just a single line (the scatter3d plot). spin3d.axis is used to set the axis parameter to spin3d; spin3d.rpm is used to set the rpm parameter. The number of images and the interval between images is set using the standard fig.num and interval parameters.

\documentclass{article}
\usepackage{animate}

<< label = setup, include = FALSE>>=
  library("rgl")
library("car")
library("knitr")
hook_rgl_spin <- 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")
    }
    
    hook_plot_custom(before, options, envir)
  }
}
knit_hooks$set(rgl = hook_rgl_spin)
@ 
  \begin{document}

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionMYaseen208View Question on Stackoverflow
Solution 1 - RNick KennedyView Answer on Stackoverflow