Skip to content

Commit

Permalink
Ready for CRAN
Browse files Browse the repository at this point in the history
  • Loading branch information
thomasp85 committed Feb 11, 2016
1 parent 62ae8d9 commit f13f476
Show file tree
Hide file tree
Showing 21 changed files with 551 additions and 76 deletions.
15 changes: 10 additions & 5 deletions DESCRIPTION
@@ -1,15 +1,19 @@
Package: tweenr
Type: Package
Title: Interpolate data for smooth animations
Version: 0.0.1
Date: 2016-02-08
Title: Interpolate Data for Smooth Animations
Version: 0.1.1
Date: 2016-02-11
Authors@R: c(
person('Thomas Lin', 'Pedersen', , 'thomasp85@gmail.com', c('aut', 'cre'))
)
Maintainer: Thomas Lin Pedersen <thomasp85@gmail.com>
Description: In order to create smooth animation between states of data,
tweening is necessary. tweenr provides a range of function for creating
tweened data that plugs right in to tools such as gganimate.
tweened data that plugs right in to tools such as gganimate. Furthermore it
adds a number of vectorized interpolaters for common R data types such as
numeric, date and colour.
URL: https://github.com/thomasp85/tweenr
BugReports: https://github.com/thomasp85/tweenr/issues
License: GPL (>=2)
LazyData: TRUE
Imports:
Expand All @@ -19,11 +23,12 @@ LinkingTo: Rcpp
Collate:
'RcppExports.R'
'aaa.R'
'tween_numeric.R'
'display_ease.R'
'interpolate.R'
'tween_colour.R'
'tween_datetime.R'
'tween_date.R'
'tween_numeric.R'
'tween.R'
'tween_appear.R'
'tween_constant.R'
Expand Down
3 changes: 3 additions & 0 deletions NAMESPACE
@@ -1,5 +1,6 @@
# Generated by roxygen2: do not edit by hand

export(display_ease)
export(tween)
export(tween_appear)
export(tween_color)
Expand All @@ -21,4 +22,6 @@ importFrom(Rcpp,sourceCpp)
importFrom(grDevices,col2rgb)
importFrom(grDevices,convertColor)
importFrom(grDevices,rgb)
importFrom(graphics,plot)
importFrom(utils,head)
useDynLib(tweenr)
60 changes: 60 additions & 0 deletions R/display_ease.R
@@ -0,0 +1,60 @@
#' @include tween_numeric.R
NULL

#' Display an easing function
#'
#' This simple helper lets you explore how the different easing functions govern
#' the interpolation of data.
#'
#' @details
#' How transitions proceed between states are defined by an easing function. The
#' easing function converts the parameterized progression from one state to the
#' next to a new number between 0 and 1. \code{linear} easing is equivalent to
#' an identity function that returns the input unchanged. In addition there are
#' a range of additional easers available, each with three modifiers.
#'
#' \strong{Easing modifiers:}
#' \describe{
#' \item{-in}{The easing function is applied as-is}
#' \item{-out}{The easing function is applied in reverse}
#' \item{-in-out}{The first half of the transition it is applied as-is, while
#' in the last half it is reversed}
#' }
#'
#' \strong{Easing functions}
#' \describe{
#' \item{quadratic}{Models a power-of-2 function}
#' \item{cubic}{Models a power-of-3 function}
#' \item{quartic}{Models a power-of-4 function}
#' \item{quintic}{Models a power-of-5 function}
#' \item{sine}{Models a sine function}
#' \item{circular}{Models a pi/2 circle arc}
#' \item{exponential}{Models an exponential function}
#' \item{elastic}{Models an elastic release of energy}
#' \item{back}{Models a pullback and relase}
#' \item{bounce}{Models the bouncing of a ball}
#' }
#'
#' In addition to this function a good animated explanation can be found
#' \href{http://easings.net}{here}.
#'
#' @param ease The name of the easing function to display (see details)
#'
#' @return This function is called for its side effects
#'
#' @examples
#' # The default - identity
#' display_ease('linear')
#'
#' # A more fancy easer
#' display_ease('elastic-in')
#'
#' @importFrom graphics plot
#' @export
#'
display_ease <- function(ease) {
easepoints <- tween_numeric(c(0, 1), 100, ease)[[1]]
progress <- seq(0, 1, length.out = 100)
plot(progress, easepoints, type = 'l', main = ease, xlab = 'In',
ylab = 'Out', bty = 'n')
}
71 changes: 70 additions & 1 deletion R/tween.R
Expand Up @@ -5,7 +5,75 @@
#'
NULL

#' Create simple tweens
#'
#' This set of functions can be used to interpolate between single data types,
#' i.e. data not part of data.frames but stored in vectors. All functions come
#' in two flavours: the standard and a *_t version. The standard reads the data
#' as a list of states, each tween matched element-wise from state to state. The
#' *_t version uses the transposed representation where each element is a vector
#' of states. The standard approach can be used when each tween has the same
#' number of states and you want to control the number of point in each state
#' transition. The latter is useful when each tween consists of different
#' numbers of states and/or you want to specify the total number of points for
#' each tween.
#'
#' @section Difference Between \code{tween_numeric} and \code{\link[stats]{approx}}:
#' \code{tween_numeric} (and \code{tween_numeric_t}) is superficially equivalent to
#' \code{\link[stats]{approx}}, but there are differences.
#' \code{\link[stats]{approx}} will create evenly spaced points, at the expense
#' of not including the actual points in the input, while the reverse is true
#' for \code{tween_numeric}. Apart from that \code{tween_numeric} of course supports easing
#' functions and is vectorized.
#'
#' @details
#' \code{tween} and \code{tween_t} are wrappers around the other functions that tries to guess
#' the type of input data and choose the appropriate tween function. Unless you
#' have data that could be understood as a colour but is in fact a character
#' vector it should be safe to use these wrappers. It is probably safer and more
#' verbose to use the explicit functions within package code as they circumvent
#' the type inference and checks whether the input data matches the tween
#' function.
#'
#' \code{tween_numeric} will provide a linear interpolation between the points based on
#' the sequence returned by the easing function. \code{tween_date} and \code{tween_datetime}
#' converts to numeric, produces the tweening, and converts back again.
#' \code{tween_colour} converts colours into Lab and does the interpolation there,
#' converting back to sRGB after the tweening is done. \code{tween_constant} is a
#' catchall that converts the input into character and interpolates by switching
#' between states halfway through the transition.
#'
#' The meaning of the \code{n} and \code{ease} arguments differs somewhat
#' between the standard and *_t versions of the functions. In the standard
#' function \code{n} and \code{ease} refers to the length and easing function of
#' each transition, being recycled if necessary to \code{length(data) - 1}. In
#' the *_t functions \code{n} and \code{ease} refers to the total length of each
#' tween and the easing function to be applied to all transition for each tween.
#' The will both be recycled to \code{length(data)}.
#'
#' @param data A list of vectors or a single vector. In the standard functions
#' each element in the list must be of equal length; for the *_t functions
#' lengths can differ. If a single vector is used it will be eqivalent to using
#' \code{as.list(data)} for the standard functions and \code{list(data)} for the
#' *_t functions.
#'
#' @param n The number of elements per transition or tween. See details
#'
#' @param ease The easing function to use for each transition or tween. See
#' details. Defaults to \code{'linear'}
#'
#' @return A list with an element for each tween. That means that the length of
#' the return is equal to the length of the elements in \code{data} for the
#' standard functions and equal to the length of \code{data} for the *_t
#' functions.
#'
#' @examples
#' tween_numeric(list(1:3, 10:8, c(20, 60, 30)), 10)
#'
#' tween_colour_t(list(colours()[1:4], colours()[1:2], colours()[25:100]), 100)
#'
#' @export
#'
tween <- function(data, n, ease = 'linear') {
type <- guessType(data)
switch(
Expand All @@ -17,6 +85,7 @@ tween <- function(data, n, ease = 'linear') {
tween_constant_t(data, n, ease)
)
}
#' @rdname tween
#' @export
tween_t <- function(data, n, ease = 'linear') {
type <- guessType(data)
Expand All @@ -29,7 +98,7 @@ tween_t <- function(data, n, ease = 'linear') {
tween_constant_t(data, n, ease)
)
}

#' @importFrom utils head
guessType <- function(data) {
data <- unlist(data)
if (is.character(data)) {
Expand Down
11 changes: 11 additions & 0 deletions R/tween_appear.R
Expand Up @@ -23,6 +23,17 @@
#' @return A data.frame as \code{data} but repeated \code{nframes} times and
#' with the additional columns \code{.age} and \code{.frame}
#'
#' @family data.frame tween
#'
#' @examples
#' data <- data.frame(
#' x = rnorm(100),
#' y = rnorm(100),
#' time = sample(50, 100, replace = TRUE)
#' )
#'
#' data <- tween_appear(data, 'time', nframes = 200)
#'
#' @export
#'
tween_appear <- function(data, time, timerange, nframes) {
Expand Down
8 changes: 8 additions & 0 deletions R/tween_colour.R
Expand Up @@ -2,6 +2,8 @@
#'
NULL

#' @rdname tween
#'
#' @export
tween_colour <- function(data, n, ease = 'linear') {
data <- as.list(data)
Expand All @@ -13,9 +15,13 @@ tween_colour <- function(data, n, ease = 'linear') {
unname(split(tweendata,
rep(seq_along(data[[1]]), length.out = length(tweendata))))
}
#' @rdname tween
#'
#' @export
tween_color <- tween_colour

#' @rdname tween
#'
#' @export
tween_colour_t <- function(data, n, ease = 'linear') {
if (!is.list(data)) {
Expand All @@ -29,5 +35,7 @@ tween_colour_t <- function(data, n, ease = 'linear') {
unname(split(tweendata,
rep(seq_along(data), rep(n, length.out = length(data)))))
}
#' @rdname tween
#'
#' @export
tween_color_t <- tween_colour_t
4 changes: 4 additions & 0 deletions R/tween_constant.R
Expand Up @@ -2,6 +2,8 @@
#'
NULL

#' @rdname tween
#'
#' @export
tween_constant <- function(data, n, ease = 'linear') {
data <- as.list(data)
Expand All @@ -12,6 +14,8 @@ tween_constant <- function(data, n, ease = 'linear') {
rep(seq_along(data[[1]]), length.out = length(tweendata))))
}

#' @rdname tween
#'
#' @export
tween_constant_t <- function(data, n, ease = 'linear') {
if (!is.list(data)) {
Expand Down
4 changes: 4 additions & 0 deletions R/tween_date.R
Expand Up @@ -2,6 +2,8 @@
#'
NULL

#' @rdname tween
#'
#' @export
tween_date <- function(data, n, ease = 'linear') {
data <- as.list(data)
Expand All @@ -14,6 +16,8 @@ tween_date <- function(data, n, ease = 'linear') {
rep(seq_along(data[[1]]), length.out = length(tweendata))))
}

#' @rdname tween
#'
#' @export
tween_date_t <- function(data, n, ease = 'linear') {
if (!is.list(data)) {
Expand Down
4 changes: 4 additions & 0 deletions R/tween_datetime.R
Expand Up @@ -2,6 +2,8 @@
#'
NULL

#' @rdname tween
#'
#' @export
tween_datetime <- function(data, n, ease = 'linear') {
data <- as.list(data)
Expand All @@ -14,6 +16,8 @@ tween_datetime <- function(data, n, ease = 'linear') {
rep(seq_along(data[[1]]), length.out = length(tweendata))))
}

#' @rdname tween
#'
#' @export
tween_datetime_t <- function(data, n, ease = 'linear') {
if (!is.list(data)) {
Expand Down
49 changes: 46 additions & 3 deletions R/tween_elements.R
Expand Up @@ -2,7 +2,50 @@
#'
NULL

#' Create frames based on individual element states
#'
#' This function creates tweens for each observation individually, in cases
#' where the data doesn't pass through collective states but consists of fully
#' independent transitions. Each observation is identified by an id and each
#' state must have a time associated with it.
#'
#' @param data A data.frame consisting at least of a column giving the
#' observation id, a column giving timepoints for each state and a column giving
#' the easing to apply when transitioning away from the state.
#'
#' @param time The name of the column holding timepoints
#'
#' @param group The name of the column holding the observation id
#'
#' @param ease The name of the column holding the easing function name
#'
#' @param timerange The range of time to span. If missing it will default to
#' \code{range(data[[time]])}
#'
#' @param nframes The number of frames to generate. If missing it will default
#' to \code{ceiling(diff(timerange) + 1)} (At least one frame for each
#' individual timepoint)
#'
#' @return A data.frame with the same columns as \code{data} except for the
#' group and ease columns, but replicated \code{nframes} times. Two additional
#' columns called \code{.frame} and \code{.group} will be added giving the frame
#' number and observation id for each row.
#'
#' @family data.frame tween
#'
#' @examples
#' data <- data.frame(
#' x = c(1, 2, 2, 1, 2, 2),
#' y = c(1, 2, 2, 2, 1, 1),
#' time = c(1, 4, 10, 4, 8, 10),
#' group = c(1, 1, 1, 2, 2, 2),
#' ease = rep('cubic-in-out', 6)
#' )
#'
#' data <- tween_elements(data, 'time', 'group', 'ease', nframes = 100)
#'
#' @export
#'
tween_elements <- function(data, time, group, ease, timerange, nframes) {
if (!all(data[[ease]] %in% validEase)) {
stop("All names given in the easing column must be valid easers")
Expand All @@ -16,15 +59,15 @@ tween_elements <- function(data, time, group, ease, timerange, nframes) {
}
framelength <- diff(timerange) / nframes
specialCols <- c(group, ease)
data <- data[order(data[[group]], data[[time]]), ]
group <- as.character(data[[group]])
data <- data[order(group, data[[time]]), ]
frame <- round((data$time - timerange[1]) / framelength)
ease <- as.character(data[[ease]])
data <- data[, !names(data) %in% specialCols, drop = FALSE]

colClasses <- col_classes(data)
tweendata <- lapply(seq_along(data), function(i) {
d <- d[[i]]
d <- data[[i]]
switch(
colClasses[i],
numeric = interpolate_numeric_element(d, group, frame, ease),
Expand All @@ -42,5 +85,5 @@ tween_elements <- function(data, time, group, ease, timerange, nframes) {
tweendata$.frame <- tweenInfo$frame
tweendata$.group <- tweenInfo$group
attr(tweendata, 'framelength') <- framelength
tweendata
tweendata[order(tweendata$.frame, tweendata$.group), ]
}

0 comments on commit f13f476

Please sign in to comment.