Search code examples
rsvgr-grid

How to animate points with gridSVG in a vectorised way?


I'm getting started learning how to use the gridSVG package to create animated SVG plots. As a test, I want to animate a set of points, moving from start positions to finish positions. The example is loosely based upon those in the package's first vignette.

First, some data:

n_points <- 20
start <- data.frame(
  x = runif(n_points),
  y = runif(n_points)
)
end <- data.frame(
    x = runif(n_points),
    y = runif(n_points)
)

The basic idea seems to be "draw a new page, add the contents for the first frame, animate, then save to SVG". My first attempt draws all the points in the same place. I'm not sure how to tell grid.animate that each point needs to be moved individually.

grid.newpage()
with(start, 
  grid.points(x, y, default.units = "npc", name = "points")
)
grid.animate(
  "points", 
  x = c(start$x, end$x),
  y = c(start$y, end$y)
)
gridToSVG("point_test1.svg")

I can get round this by drawing each point in its own grob, using lapply. This works, but feels clunky – there ought to be a vectorised way of doing it.

grid.newpage()
lapply(seq_len(n_points), function(i)
{
  gname = paste("points", i, sep = ".")
  with(start, 
    grid.points(x[i], y[i], default.units = "npc", name = gname)
  )
  grid.animate(
    gname, 
    x = c(start$x[i], end$x[i]),
    y = c(start$y[i], end$y[i])
  )
})
gridToSVG("point_test2.svg")

I also tried using animUnit, but I can't figure out how to specify the id parameter.

grid.newpage()
with(start, 
  grid.points(x, y, default.units = "npc", name = "points")
)
x_points <- animUnit(
  unit(c(start$x, end$x), "npc"),
  timeid = rep(1:2, each = n_points),
  id = rep(1:2, n_points)
)        
y_points <- animUnit(
  unit(c(start$y, end$y), "npc"),
  timeid = rep(1:2, each = n_points),
  id = rep(1:2, n_points)
)        
grid.animate(  #throws an error: Expecting only one value per time point
  "points", 
  x = x_points,
  y = y_points
)
gridToSVG("point_test3.svg")

Can I animate multiple points inside a single grob, or otherwise animate points without a loop?


Solution

  • This works for me using animUnit:

    grid.newpage()
    with(start, 
      grid.points(x, y, default.units = "npc", name = "points")
    )
    x_points <- animUnit(
      unit(c(start$x, end$x), "npc"),
      #timeid = rep(1:2, each = n_points),
      id = rep(1:20, 2)
    )        
    y_points <- animUnit(
      unit(c(start$y, end$y), "npc"),
      #timeid = rep(1:2, each = n_points),
      id = rep(1:20, 2)
    )        
    grid.animate(  #throws an error: Expecting only one value per time point
      "points", 
      x = x_points,
      y = y_points
    )
    

    You were specifying id's for two points, rather than twenty. My reading of the vignette is that for animating multiple grobs with a single x value per grob, you only need to specify the id.