From 520f0dbb9b4fbb3630e8de684652834ee71b4e99 Mon Sep 17 00:00:00 2001 From: Toby Dylan Hocking Date: Thu, 3 Nov 2016 08:07:38 -0400 Subject: [PATCH 1/6] Inf test fails --- tests/testthat/test-renderer4-Inf.R | 65 +++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 tests/testthat/test-renderer4-Inf.R diff --git a/tests/testthat/test-renderer4-Inf.R b/tests/testthat/test-renderer4-Inf.R new file mode 100644 index 00000000..b67f823d --- /dev/null +++ b/tests/testthat/test-renderer4-Inf.R @@ -0,0 +1,65 @@ +acontext("Inf") + +limits <- data.frame( + i=1:3, + lower=c(-Inf, 0, -1), + upper=c(1, 2, Inf)) +viz <- list( + gg=ggplot()+ + theme_bw()+ + theme(panel.margin=grid::unit(0, "lines"))+ + facet_grid(side ~ top)+ + geom_segment(aes( + i, lower, yend=upper, xend=i), + data=data.frame(limits, side="yInf", top="left"))+ + geom_segment(aes( + lower, i, xend=upper, yend=i), + data=data.frame(limits, side="xInf", top="right"))) +info <- animint2HTML(viz) + +## First panel, test y values. +bg.rect <- getNodeSet( + info$html, + "//g[contains(@class, 'bgr1')]//rect[@class='background_rect']")[[1]] +attr.vec <- xmlAttrs(bg.rect) +panel.top <- as.numeric(attr.vec[["y"]]) +h <- as.numeric(attr.vec[["height"]]) +panel.bottom <- panel.top + h +line.list <- getNodeSet( + info$html, + "//g[contains(@class, 'PANEL1')]//line") +attr.mat <- sapply(line.list, xmlAttrs) +seg.bottom <- as.numeric(attr.mat["y1",]) +seg.top <- as.numeric(attr.mat["y2",]) +n.top <- sum(seg.top == panel.top) +n.bottom <- sum(seg.bottom == panel.bottom) +test_that("one y at top of panel", { + expect_equal(n.top, 1) +}) +test_that("one y at bottom of panel", { + expect_equal(n.bottom, 1) +}) + +## Last panel, test x values. +bg.rect <- getNodeSet( + info$html, + "//g[contains(@class, 'bgr4')]//rect[@class='background_rect']")[[1]] +attr.vec <- xmlAttrs(bg.rect) +panel.left <- as.numeric(attr.vec[["x"]]) +w <- as.numeric(attr.vec[["width"]]) +panel.right <- panel.left + w +line.list <- getNodeSet( + info$html, + "//g[contains(@class, 'PANEL4')]//line") +attr.mat <- sapply(line.list, xmlAttrs) +seg.left <- as.numeric(attr.mat["x1",]) +seg.right <- as.numeric(attr.mat["x2",]) +n.left <- sum(seg.left == panel.left) +n.right <- sum(seg.right == panel.right) +test_that("one x at left of panel", { + expect_equal(n.left, 1) +}) +test_that("one x at right of panel", { + expect_equal(n.right, 1) +}) + From 121b9ec54137ce9dc5c19b624735dcc6e456eb9f Mon Sep 17 00:00:00 2001 From: faizan Date: Tue, 27 Dec 2016 21:26:31 +0530 Subject: [PATCH 2/6] clean parsePlot --- R/animint.R | 93 ++++++++++++++++++++--------------------------------- 1 file changed, 34 insertions(+), 59 deletions(-) diff --git a/R/animint.R b/R/animint.R index fd199620..d23fb3c1 100644 --- a/R/animint.R +++ b/R/animint.R @@ -3,41 +3,23 @@ #' @return nothing, info is stored in meta. #' @export #' @import ggplot2 plyr -parsePlot <- function(meta){ +parsePlot <- function(meta, plot, plot.name){ ## adding data and mapping to each layer from base plot, if necessary - for(layer.i in seq_along(meta$plot$layers)) { + for(layer.i in seq_along(plot$layers)) { ## if data is not specified, get it from plot - if(length(meta$plot$layers[[layer.i]]$data) == 0){ - meta$plot$layers[[layer.i]]$data <- meta$plot$data + if(length(plot$layers[[layer.i]]$data) == 0){ + plot$layers[[layer.i]]$data <- plot$data } ## if mapping is not specified, get it from plot - if(is.null(meta$plot$layers[[layer.i]]$mapping)){ - meta$plot$layers[[layer.i]]$mapping <- meta$plot$mapping + if(is.null(plot$layers[[layer.i]]$mapping)){ + plot$layers[[layer.i]]$mapping <- plot$mapping } - } - - meta$built <- ggplot2::ggplot_build(meta$plot) + built <- ggplot2::ggplot_build(plot) plot.meta <- list() - scaleFuns <- - list(manual=function(sc)sc$palette(0), - brewer=function(sc)sc$palette(length(sc$range$range)), - hue=function(sc)sc$palette(length(sc$range$range)), - linetype_d=function(sc)sc$palette(length(sc$range$range)), - alpha_c=function(sc)sc$palette(sc$range$range), - size_c=function(sc)sc$palette(sc$range$range), - gradient=function(sc){ - ggplot2:::scale_map(sc, ggplot2:::scale_breaks(sc)) - }) - for(sc in meta$plot$scales$scales){ - if(!is.null(sc$range$range)){ - makeScale <- scaleFuns[[sc$scale_name]] - plot.meta$scales[[sc$aesthetics]] <- makeScale(sc) - } - } ## Export axis specification as a combination of breaks and ## labels, on the relevant axis scale (i.e. so that it can @@ -65,10 +47,10 @@ parsePlot <- function(meta){ } ## scan for legends in each layer. - for(layer.i in seq_along(meta$plot$layers)){ + for(layer.i in seq_along(plot$layers)){ ##cat(sprintf("%4d / %4d layers\n", layer.i, length(meta$plot$layers))) ## This is the layer from the original ggplot object. - L <- meta$plot$layers[[layer.i]] + L <- plot$layers[[layer.i]] ## If any legends are specified, add showSelected aesthetic for(legend.i in seq_along(plot.meta$legend)) { one.legend <- plot.meta$legend[[legend.i]] @@ -128,23 +110,17 @@ parsePlot <- function(meta){ ## we need to specify the variable corresponding to each legend. ## To do this, we need to have the legend. ## And to have the legend, I think that we need to use ggplot_build - meta$built <- ggplot2::ggplot_build(meta$plot) + meta$built <- ggplot2::ggplot_build(plot) + ## TODO: implement a compiler that does not call ggplot_build at ## all, and instead does all of the relevant computations in animint ## code. ## 'strips' are really titles for the different facet panels - plot.meta$strips <- with(meta$built, getStrips(plot$facet, panel)) + plot.meta$strips <- with(built, getStrips(plot$facet, panel)) ## the layout tells us how to subset and where to plot on the JS side - plot.meta$layout <- with(meta$built, flag_axis(plot$facet, panel$layout)) - plot.meta$layout <- with(meta$built, train_layout( + plot.meta$layout <- with(built, flag_axis(plot$facet, panel$layout)) + plot.meta$layout <- with(built, train_layout( plot$facet, plot$coordinates, plot.meta$layout, panel$ranges)) - - ## Export axis specification as a combination of breaks and - ## labels, on the relevant axis scale (i.e. so that it can - ## be passed into d3 on the x axis scale instead of on the - ## grid 0-1 scale). This allows transformations to be used - ## out of the box, with no additional d3 coding. - theme.pars <- ggplot2:::plot_theme(meta$plot) ## extract panel background and borders from theme.pars get_bg <- function(pars) { @@ -196,11 +172,11 @@ parsePlot <- function(meta){ ## x and y locations if(major) { - pars$loc$x <- as.list(meta$built$panel$ranges[[1]]$x.major_source) - pars$loc$y <- as.list(meta$built$panel$ranges[[1]]$y.major_source) + pars$loc$x <- as.list(built$panel$ranges[[1]]$x.major_source) + pars$loc$y <- as.list(built$panel$ranges[[1]]$y.major_source) } else { - pars$loc$x <- as.list(meta$built$panel$ranges[[1]]$x.minor_source) - pars$loc$y <- as.list(meta$built$panel$ranges[[1]]$y.minor_source) + pars$loc$x <- as.list(built$panel$ranges[[1]]$x.minor_source) + pars$loc$y <- as.list(built$panel$ranges[[1]]$y.minor_source) ## remove minor lines when major lines are already drawn pars$loc$x <- pars$loc$x[ !(pars$loc$x %in% plot.meta$grid_major$loc$x) @@ -220,20 +196,20 @@ parsePlot <- function(meta){ ## Flip labels if coords are flipped - transform does not take care ## of this. Do this BEFORE checking if it is blank or not, so that ## individual axes can be hidden appropriately, e.g. #1. - if("CoordFlip"%in%attr(meta$plot$coordinates, "class")){ - temp <- meta$plot$labels$x - meta$plot$labels$x <- meta$plot$labels$y - meta$plot$labels$y <- temp + if("CoordFlip"%in%attr(plot$coordinates, "class")){ + temp <- plot$labels$x + plot$labels$x <- plot$labels$y + plot$labels$y <- temp } is.blank <- function(el.name){ - x <- ggplot2::calc_element(el.name, meta$plot$theme) + x <- ggplot2::calc_element(el.name, plot$theme) "element_blank"%in%attr(x,"class") } # Instead of an "axis" JSON object for each plot, # allow for "axis1", "axis2", etc. where # "axis1" corresponds to the 1st PANEL - ranges <- meta$built$panel$ranges + ranges <- built$panel$ranges n.axis <- length(ranges) axes <- setNames(vector("list", n.axis), paste0("axis", seq_len(n.axis))) @@ -246,12 +222,12 @@ parsePlot <- function(meta){ plot.meta[[s("%stitle")]] <- if(is.blank(s("axis.title.%s"))){ "" } else { - scale.i <- which(meta$plot$scales$find(xy)) + scale.i <- which(plot$scales$find(xy)) lab.or.null <- if(length(scale.i) == 1){ - meta$plot$scales$scales[[scale.i]]$name + plot$scales$scales[[scale.i]]$name } if(is.null(unlist(lab.or.null))){ - meta$plot$labels[[xy]] + plot$labels[[xy]] }else{ lab.or.null } @@ -299,13 +275,13 @@ parsePlot <- function(meta){ if("element_blank"%in%attr(theme.pars$plot.title, "class")){ plot.meta$title <- "" } else { - plot.meta$title <- meta$plot$labels$title + plot.meta$title <- plot$labels$title } ## Set plot width and height from animint.* options if they are ## present. plot.meta$options <- list() - theme <- meta$plot$theme + theme <- plot$theme for(wh in c("width", "height")){ awh <- paste0("animint.", wh) plot.meta$options[[wh]] <- if(awh %in% names(theme)){ @@ -320,11 +296,11 @@ parsePlot <- function(meta){ plot.meta$options$update_axes <- theme[[update_axes]] } - meta$plots[[meta$plot.name]] <- plot.meta + meta$plots[[plot.name]] <- plot.meta list( - ggplot=meta$plot, - built=meta$built) + ggplot=plot, + built=built) } hjust2anchor <- function(hjust){ @@ -1438,9 +1414,8 @@ animint2dir <- function(plot.list, out.dir = tempfile(), stop("aes(clickSelects) can not be used with aes(href)") } } - meta$plot <- p - meta$plot.name <- list.name - ggplot.list[[list.name]] <- parsePlot(meta) # calls ggplot_build. + + ggplot.list[[list.name]] <- parsePlot(meta, p, list.name) # calls ggplot_build }else if(is.list(p)){ ## for options. meta[[list.name]] <- p }else{ From c6a3c9259b4ca1a64d9f862b2b6ee621881d8827 Mon Sep 17 00:00:00 2001 From: faizan Date: Tue, 27 Dec 2016 21:42:04 +0530 Subject: [PATCH 3/6] clean up saveLayer --- R/animint.R | 34 +++++++++++++++------------------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/R/animint.R b/R/animint.R index d23fb3c1..05b5454a 100644 --- a/R/animint.R +++ b/R/animint.R @@ -327,16 +327,16 @@ hjust2anchor <- function(hjust){ #' @param meta environment of meta-data. #' @return list representing a layer, with corresponding aesthetics, ranges, and groups. #' @export -saveLayer <- function(l, d, meta){ +saveLayer <- function(l, d, meta, p.name, ggplot, built){ # carson's approach to getting layer types ggtype <- function (x, y = "geom") { sub(y, "", tolower(class(x[[y]])[1])) } - ranges <- meta$built$panel$ranges + ranges <- built$panel$ranges g <- list(geom=ggtype(l)) g$classed <- sprintf("geom%d_%s_%s", - meta$geom.count, g$geom, meta$plot.name) + meta$geom.count, g$geom, p.name) ## For each geom, save the nextgeom to preserve drawing order. if(is.character(meta$prev.class)){ meta$geoms[[meta$prev.class]]$nextgeom <- g$classed @@ -417,7 +417,7 @@ saveLayer <- function(l, d, meta){ s.aes$clickSelects$ignored) copy.cols <- ! names(d) %in% do.not.copy g.data <- d[copy.cols] - + is.ss <- names(g$aes) %in% s.aes$showSelected$one show.vars <- g$aes[is.ss] pre.subset.order <- as.list(names(show.vars)) @@ -428,7 +428,7 @@ saveLayer <- function(l, d, meta){ update.var.names <- if(0 < length(update.vars)){ data.frame(variable=names(update.vars), value=NA) } - + interactive.aes <- with(s.aes, { rbind(clickSelects$several, showSelected$several, update.var.names) @@ -702,7 +702,7 @@ saveLayer <- function(l, d, meta){ col.names } - if(inherits(meta$plot$coordinates, "CoordFlip")){ + if(inherits(ggplot$coordinates, "CoordFlip")){ names(g.data) <- switch_axes(names(g.data)) } @@ -875,9 +875,9 @@ saveLayer <- function(l, d, meta){ } } # meta$selectors > 0 } - + # If there is only one PANEL, we don't need it anymore. - plot.has.panels <- nrow(meta$built$panel$layout) > 1 + plot.has.panels <- nrow(built$panel$layout) > 1 g$PANEL <- unique(g.data[["PANEL"]]) geom.has.one.panel <- length(g$PANEL) == 1 if(geom.has.one.panel && (!plot.has.panels)) { @@ -900,7 +900,7 @@ saveLayer <- function(l, d, meta){ names(g$chunk_order) <- NULL names(g$nest_order) <- NULL g$subset_order <- g$nest_order - + ## If this plot has more than one PANEL then add it to subset_order ## and nest_order. if(plot.has.panels){ @@ -917,7 +917,7 @@ saveLayer <- function(l, d, meta){ g$subset_order <- c(g$subset_order, paste(s.aes$showSelected$several$variable)) } - + ## group should be the last thing in nest_order, if it is present. data.object.geoms <- c("line", "path", "ribbon", "polygon") if("group" %in% names(g$aes) && g$geom %in% data.object.geoms){ @@ -1436,14 +1436,6 @@ animint2dir <- function(plot.list, out.dir = tempfile(), ## layer.i, length(ggplot.info$built$data), ## p.name)) - ## This is a total hack, we should clean up the internals - ## (parsePlot, saveLayer) so that they no longer rely on this - ## meta object which makes it super confusing to know which - ## functions need which data. - meta$plot.name <- p.name - meta$plot <- ggplot.info$ggplot - meta$built <- ggplot.info$built - ## Data now contains columns with fill, alpha, colour etc. ## Remove from data if they have a single unique value and ## are NOT used in mapping to reduce tsv file size @@ -1461,7 +1453,11 @@ animint2dir <- function(plot.list, out.dir = tempfile(), } } - g <- saveLayer(L, df, meta) + ## This is a total hack, we should clean up the internals + ## (parsePlot, saveLayer) so that they no longer rely on this + ## meta object which makes it super confusing to know which + ## functions need which data. + g <- saveLayer(L, df, meta, p.name, ggplot.info$ggplot, ggplot.info$built) ## Every plot has a list of geom names. meta$plots[[p.name]]$geoms <- c( From 232a5ea4aa1bf6ebcceada91f2586f9d48135911 Mon Sep 17 00:00:00 2001 From: faizan Date: Tue, 27 Dec 2016 21:52:36 +0530 Subject: [PATCH 4/6] break up saveLayer --- R/animint.R | 44 +++++++++++++++++++++++++++++--------------- 1 file changed, 29 insertions(+), 15 deletions(-) diff --git a/R/animint.R b/R/animint.R index 05b5454a..744a9c43 100644 --- a/R/animint.R +++ b/R/animint.R @@ -321,6 +321,18 @@ hjust2anchor <- function(hjust){ } } +storeLayer <- function(meta, g, g.data.varied){ + ## Save each variable chunk to a separate tsv file. + meta$chunk.i <- 1L + meta$g <- g + g$chunks <- saveChunks(g.data.varied, meta) + g$total <- length(unlist(g$chunks)) + + ## Finally save to the master geom list. + meta$geoms[[g$classed]] <- g + g +} + #' Save a layer to disk, save and return meta-data. #' @param l one layer of the ggplot object. #' @param d one layer of calculated data from ggplot2::ggplot_build(p). @@ -960,16 +972,7 @@ saveLayer <- function(l, d, meta, p.name, ggplot, built){ data.or.null$varied } - ## Save each variable chunk to a separate tsv file. - meta$chunk.i <- 1L - meta$g <- g - g$chunks <- saveChunks(g.data.varied, meta) - g$total <- length(unlist(g$chunks)) - - ## Finally save to the master geom list. - meta$geoms[[g$classed]] <- g - - g + list(g=g, g.data.varied=g.data.varied) } ##' Save the common columns for each tsv to one chunk @@ -1425,6 +1428,7 @@ animint2dir <- function(plot.list, out.dir = tempfile(), ## After going through all of the meta-data in all of the ggplots, ## now we have enough info to save the TSV file database. + g.list <- list() for(p.name in names(ggplot.list)){ ggplot.info <- ggplot.list[[p.name]] meta$prev.class <- NULL # first geom of any plot should not be next. @@ -1457,11 +1461,11 @@ animint2dir <- function(plot.list, out.dir = tempfile(), ## (parsePlot, saveLayer) so that they no longer rely on this ## meta object which makes it super confusing to know which ## functions need which data. - g <- saveLayer(L, df, meta, p.name, ggplot.info$ggplot, ggplot.info$built) - - ## Every plot has a list of geom names. - meta$plots[[p.name]]$geoms <- c( - meta$plots[[p.name]]$geoms, list(g$classed)) + gl <- saveLayer(L, df, meta, p.name, ggplot.info$ggplot, ggplot.info$built) + + ## Save to a list before saving to tsv + ## Helps during axis updates and Inf values + g.list[[p.name]][[gl$g$classed]] <- gl }#layer.i } @@ -1570,6 +1574,16 @@ animint2dir <- function(plot.list, out.dir = tempfile(), } } + ## Finally save all the layers + for(p.name in names(ggplot.list)){ + for(g1 in seq_along(g.list[[p.name]])){ + g <- storeLayer(meta, g.list[[p.name]][[g1]]$g, g.list[[p.name]][[g1]]$g.data.varied) + ## Every plot has a list of geom names. + meta$plots[[p.name]]$geoms <- c( + meta$plots[[p.name]]$geoms, list(g$classed)) + }#layer.i + } + ## These geoms need to be updated when the time.var is animated, so ## let's make a list of all possible values to cycle through, from ## all the values used in those geoms. From c42ec5b0e1eb3dfadf7708902f1da5f0346afb9f Mon Sep 17 00:00:00 2001 From: faizan Date: Tue, 27 Dec 2016 22:12:01 +0530 Subject: [PATCH 5/6] reposition compute domains --- R/animint.R | 172 ++++++++++++++++++++++++++-------------------------- 1 file changed, 86 insertions(+), 86 deletions(-) diff --git a/R/animint.R b/R/animint.R index 744a9c43..71f3087e 100644 --- a/R/animint.R +++ b/R/animint.R @@ -1510,24 +1510,6 @@ animint2dir <- function(plot.list, out.dir = tempfile(), meta$selectors[[selector.name]]$update <- as.list(unique(unlist(lapply(values.update, "[[", "update")))) } - - ## Now that selectors are all defined, go back through geoms to - ## check if there are any warnings to issue. - for(g.name in names(meta$geoms)){ - g.info <- meta$geoms[[g.name]] - g.selectors <- meta$selector.aes[[g.name]] - show.vars <- g.info$aes[g.selectors$showSelected$one] - duration.vars <- names(meta$duration) - show.with.duration <- show.vars[show.vars %in% duration.vars] - no.key <- ! "key" %in% names(g.info$aes) - if(length(show.with.duration) && no.key){ - warning( - "to ensure that smooth transitions are interpretable, ", - "aes(key) should be specifed for geoms with aes(showSelected=", - show.with.duration[1], - "), problem: ", g.name) - } - } ## For a static data viz with no interactive aes, no need to check ## for trivial showSelected variables with only 1 level. @@ -1573,73 +1555,6 @@ animint2dir <- function(plot.list, out.dir = tempfile(), } } } - - ## Finally save all the layers - for(p.name in names(ggplot.list)){ - for(g1 in seq_along(g.list[[p.name]])){ - g <- storeLayer(meta, g.list[[p.name]][[g1]]$g, g.list[[p.name]][[g1]]$g.data.varied) - ## Every plot has a list of geom names. - meta$plots[[p.name]]$geoms <- c( - meta$plots[[p.name]]$geoms, list(g$classed)) - }#layer.i - } - - ## These geoms need to be updated when the time.var is animated, so - ## let's make a list of all possible values to cycle through, from - ## all the values used in those geoms. - if("time" %in% ls(meta)){ - meta$selectors[[meta$time$variable]]$type <- "single" - anim.values <- meta$timeValues - if(length(meta$timeValues)==0){ - stop("no interactive aes for time variable ", meta$time$variable) - } - anim.not.null <- anim.values[!sapply(anim.values, is.null)] - time.classes <- sapply(anim.not.null, function(x) class(x)[1]) - time.class <- time.classes[[1]] - if(any(time.class != time.classes)){ - print(time.classes) - stop("time variables must all have the same class") - } - meta$time$sequence <- if(time.class=="POSIXct"){ - orderTime <- function(format){ - values <- unlist(sapply(anim.not.null, strftime, format)) - sort(unique(as.character(values))) - } - hms <- orderTime("%H:%M:%S") - f <- if(length(hms) == 1){ - "%Y-%m-%d" - }else{ - "%Y-%m-%d %H:%M:%S" - } - orderTime(f) - }else if(time.class=="factor"){ - levs <- levels(anim.not.null[[1]]) - if(any(sapply(anim.not.null, function(f)levels(f)!=levs))){ - print(sapply(anim.not.null, levels)) - stop("all time factors must have same levels") - } - levs - }else{ #character, numeric, integer, ... what else? - as.character(sort(unique(unlist(anim.not.null)))) - } - meta$selectors[[time.var]]$selected <- meta$time$sequence[[1]] - } - - ## The first selection: - for(selector.name in names(meta$first)){ - first <- as.character(meta$first[[selector.name]]) - if(selector.name %in% names(meta$selectors)){ - s.type <- meta$selectors[[selector.name]]$type - if(s.type == "single"){ - stopifnot(length(first) == 1) - } - meta$selectors[[selector.name]]$selected <- first - }else{ - print(list(selectors=names(meta$selectors), - missing.first=selector.name)) - stop("missing first selector variable") - } - } ## Compute domains of different subsets, to be used by update_scales ## in the renderer @@ -1865,7 +1780,92 @@ animint2dir <- function(plot.list, out.dir = tempfile(), } } } - + + ## Finally save all the layers + for(p.name in names(ggplot.list)){ + for(g1 in seq_along(g.list[[p.name]])){ + g <- storeLayer(meta, g.list[[p.name]][[g1]]$g, g.list[[p.name]][[g1]]$g.data.varied) + ## Every plot has a list of geom names. + meta$plots[[p.name]]$geoms <- c( + meta$plots[[p.name]]$geoms, list(g$classed)) + }#layer.i + } + + ## Now that selectors are all defined, go back through geoms to + ## check if there are any warnings to issue. + for(g.name in names(meta$geoms)){ + g.info <- meta$geoms[[g.name]] + g.selectors <- meta$selector.aes[[g.name]] + show.vars <- g.info$aes[g.selectors$showSelected$one] + duration.vars <- names(meta$duration) + show.with.duration <- show.vars[show.vars %in% duration.vars] + no.key <- ! "key" %in% names(g.info$aes) + if(length(show.with.duration) && no.key){ + warning( + "to ensure that smooth transitions are interpretable, ", + "aes(key) should be specifed for geoms with aes(showSelected=", + show.with.duration[1], + "), problem: ", g.name) + } + } + + ## These geoms need to be updated when the time.var is animated, so + ## let's make a list of all possible values to cycle through, from + ## all the values used in those geoms. + if("time" %in% ls(meta)){ + meta$selectors[[meta$time$variable]]$type <- "single" + anim.values <- meta$timeValues + if(length(meta$timeValues)==0){ + stop("no interactive aes for time variable ", meta$time$variable) + } + anim.not.null <- anim.values[!sapply(anim.values, is.null)] + time.classes <- sapply(anim.not.null, function(x) class(x)[1]) + time.class <- time.classes[[1]] + if(any(time.class != time.classes)){ + print(time.classes) + stop("time variables must all have the same class") + } + meta$time$sequence <- if(time.class=="POSIXct"){ + orderTime <- function(format){ + values <- unlist(sapply(anim.not.null, strftime, format)) + sort(unique(as.character(values))) + } + hms <- orderTime("%H:%M:%S") + f <- if(length(hms) == 1){ + "%Y-%m-%d" + }else{ + "%Y-%m-%d %H:%M:%S" + } + orderTime(f) + }else if(time.class=="factor"){ + levs <- levels(anim.not.null[[1]]) + if(any(sapply(anim.not.null, function(f)levels(f)!=levs))){ + print(sapply(anim.not.null, levels)) + stop("all time factors must have same levels") + } + levs + }else{ #character, numeric, integer, ... what else? + as.character(sort(unique(unlist(anim.not.null)))) + } + meta$selectors[[time.var]]$selected <- meta$time$sequence[[1]] + } + + ## The first selection: + for(selector.name in names(meta$first)){ + first <- as.character(meta$first[[selector.name]]) + if(selector.name %in% names(meta$selectors)){ + s.type <- meta$selectors[[selector.name]]$type + if(s.type == "single"){ + stopifnot(length(first) == 1) + } + meta$selectors[[selector.name]]$selected <- first + }else{ + print(list(selectors=names(meta$selectors), + missing.first=selector.name)) + stop("missing first selector variable") + } + } + ## Finally, copy html/js/json files to out.dir. src.dir <- system.file("htmljs",package="animint") to.copy <- Sys.glob(file.path(src.dir, "*")) From 69a5ae284f85ba9814937439aca447ea2a7c1dd4 Mon Sep 17 00:00:00 2001 From: faizan Date: Tue, 27 Dec 2016 22:59:38 +0530 Subject: [PATCH 6/6] handle Inf vals for non updating plots --- R/animint.R | 53 +++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 41 insertions(+), 12 deletions(-) diff --git a/R/animint.R b/R/animint.R index 71f3087e..0292ad6c 100644 --- a/R/animint.R +++ b/R/animint.R @@ -26,7 +26,7 @@ parsePlot <- function(meta, plot, plot.name){ ## be passed into d3 on the x axis scale instead of on the ## grid 0-1 scale). This allows transformations to be used ## out of the box, with no additional d3 coding. - theme.pars <- ggplot2:::plot_theme(meta$plot) + theme.pars <- ggplot2:::plot_theme(plot) ## Interpret panel.margin as the number of lines between facets ## (ignoring whatever grid::unit such as cm that was specified). @@ -43,7 +43,7 @@ parsePlot <- function(meta, plot, plot.name){ ## No legend if theme(legend.postion="none"). plot.meta$legend <- if(theme.pars$legend.position != "none"){ - getLegendList(meta$built) + getLegendList(built) } ## scan for legends in each layer. @@ -110,7 +110,7 @@ parsePlot <- function(meta, plot, plot.name){ ## we need to specify the variable corresponding to each legend. ## To do this, we need to have the legend. ## And to have the legend, I think that we need to use ggplot_build - meta$built <- ggplot2::ggplot_build(plot) + built <- ggplot2::ggplot_build(plot) ## TODO: implement a compiler that does not call ggplot_build at ## all, and instead does all of the relevant computations in animint @@ -1705,32 +1705,52 @@ animint2dir <- function(plot.list, out.dir = tempfile(), gridlines } + # Replace Inf values before saving to tsv, for non updating plots + replace_infinites <- function(g.data.layer, col.range){ + for(col.name in names(g.data.layer)){ + if(grepl("^[xy]", col.name) && any(is.infinite(g.data.layer[[col.name]]))){ + g.data.layer[[col.name]][g.data.layer[[col.name]] == -Inf] <- + if(grepl("^[x]", col.name)){ + col.range$x.range[[1]] + }else{ + col.range$y.range[[1]] + } + g.data.layer[[col.name]][g.data.layer[[col.name]] == Inf] <- + if(grepl("^[x]", col.name)){ + col.range$x.range[[2]] + }else{ + col.range$y.range[[2]] + } + } + } + g.data.layer + } + ## Get domains of data subsets if theme_animint(update_axes) is used for(p.name in names(ggplot.list)){ axes_to_update <- meta$plots[[p.name]]$options$update_axes if(!is.null(axes_to_update)){ - p_geoms <- meta$plots[[p.name]]$geoms - for (axis in axes_to_update){ + for(axis in axes_to_update){ subset_domains <- list() # Determine if every panel needs a different domain or not # We conclude here if we want to split the data by PANEL # for the axes updates. Else every panel uses the same # domain - panels <- meta$plots[[p.name]]$layout$PANEL + panels <- ggplot.list[[p.name]]$built$panel$layout$PANEL axes_drawn <- - meta$plots[[p.name]]$layout[[paste0("AXIS_", toupper(axis))]] + ggplot.list[[p.name]]$built$panel$layout[[paste0("AXIS_", toupper(axis))]] panels_used <- panels[axes_drawn] split_by_panel <- all(panels == panels_used) - for(num in seq_along(p_geoms)){ + for(num in seq_along(ggplot.list[[p.name]]$built$plot$layers)){ # handle cases for showSelected: showSelectedlegendfill, # showSelectedlegendcolour etc. - aesthetic_names <- names(meta$geoms[[ p_geoms[[num]] ]]$aes) + aesthetic_names <- names(g.list[[p.name]][[num]]$g$aes) choose_ss <- grepl("^showSelected", aesthetic_names) - ss_selectors <- meta$geoms[[ p_geoms[[num]] ]]$aes[choose_ss] + ss_selectors <- g.list[[p.name]][[num]]$g$aes[choose_ss] # Do not calculate domains for multiple selectors remove_ss <- c() for(j in seq_along(ss_selectors)){ - if(meta$selectors[[ss_selectors[j]]]$type != "single"){ + if(meta$selectors[[ ss_selectors[[j]] ]]$type != "single"){ remove_ss <- c(remove_ss, ss_selectors[j]) } } @@ -1745,7 +1765,7 @@ animint2dir <- function(plot.list, out.dir = tempfile(), if(length(ss_selectors) > 0){ subset_domains[num] <- compute_domains( ggplot.list[[p.name]]$built$data[[num]], - axis, strsplit(p_geoms[[num]], "_")[[1]][[2]], + axis, strsplit(names(g.list[[p.name]])[[num]], "_")[[1]][[2]], names(sort(ss_selectors)), split_by_panel) } } @@ -1778,6 +1798,15 @@ animint2dir <- function(plot.list, out.dir = tempfile(), update_axes[!axis == update_axes] } } + }else{ + ## Handle infinite values, if any, before saving the layer in + ## plots with no axes updates + for(num_layer in seq_along(g.list[[p.name]])){ + panel.num <- g.list[[p.name]][[num_layer]]$g$PANEL + g.list[[p.name]][[num_layer]]$g.data.varied <- + replace_infinites(g.list[[p.name]][[num_layer]]$g.data.varied, + ggplot.list[[p.name]]$built$panel$ranges[[panel.num]]) + } } }