Skip to content

Commit aa643ae

Browse files
committed
don't inherit props by default; modularize evaluation; fix #280; fix #277
1 parent 5649926 commit aa643ae

File tree

2 files changed

+44
-34
lines changed

2 files changed

+44
-34
lines changed

R/plotly.R

Lines changed: 24 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@
6565
#'
6666
plot_ly <- function(data = data.frame(), ..., type = "scatter",
6767
group, color, colors, symbol, symbols, size,
68-
width = NULL, height = NULL, inherit = TRUE,
68+
width = NULL, height = NULL, inherit = FALSE,
6969
evaluate = FALSE) {
7070
# "native" plotly arguments
7171
argz <- substitute(list(...))
@@ -265,9 +265,6 @@ plotly_build <- function(l = last_plot()) {
265265
# ggplot objects don't need any special type of handling
266266
if (is.ggplot(l)) return(gg2list(l))
267267
l <- get_plot(l)
268-
# plots without NSE don't need it either
269-
nmz <- c(lapply(l$data, names), lapply(l$layout, names), lapply(l$style, names))
270-
if (!all(c("args", "env") %in% unlist(nmz))) return(structure(l, class = unique("plotly", class(l))))
271268
# assume unnamed list elements are data/traces
272269
nms <- names(l)
273270
idx <- nms %in% ""
@@ -276,14 +273,20 @@ plotly_build <- function(l = last_plot()) {
276273
} else if (any(idx)) {
277274
c(data = c(l$data, l[idx]), l[!idx])
278275
} else l
279-
dats <- list()
276+
# carry over unevaluated arguments, if necessary
277+
if (length(l$data) > 1 && isTRUE(l$data[[1]]$inherit) && should_eval(l$data[[1]])) {
278+
for (i in seq.int(2, length(l$data))) {
279+
l$data[[i]] <- modifyList(l$data[[i]], l$data[[1]])
280+
}
281+
}
282+
# 'x' is the same as 'l', but with arguments evaluated
283+
# this is ugly, but we don't know how many traces we'll have until
284+
# we evaluate arguments and call traceify() (or similar)
285+
x <- list()
280286
for (i in seq_along(l$data)) {
281287
d <- l$data[[i]]
282-
# if appropriate, evaluate trace arguments in a suitable environment
283-
idx <- names(d) %in% c("args", "env", "enclos")
284-
if (sum(idx) == 3) {
285-
dat <- c(d[!idx], eval(d$args, as.list(d$env, all.names = TRUE), d$enclos))
286-
dat[c("args", "env", "enclos")] <- NULL
288+
if (should_eval(d)) {
289+
dat <- do_eval(d)
287290
# start processing specially named arguments
288291
s <- dat[["size"]]
289292
if (!is.null(s)) {
@@ -309,21 +312,14 @@ plotly_build <- function(l = last_plot()) {
309312
has_group <- !is.null(dat[["group"]])
310313
if (has_color) {
311314
title <- as.list(d$args)[["color"]] %||% as.list(d$args)[["z"]] %||% ""
312-
dats <- c(dats, colorize(dat, title))
315+
x$data <- c(x$data, colorize(dat, title))
313316
}
314317
# TODO: add a legend title (is this only possible via annotations?!?)
315-
if (has_symbol) dats <- c(dats, symbolize(dat))
316-
if (has_group) dats <- c(dats, traceify(dat, "group"))
317-
if (!has_color && !has_symbol && !has_group) dats <- c(dats, list(dat))
318+
if (has_symbol) x$data <- c(x$data, symbolize(dat))
319+
if (has_group) x$data <- c(x$data, traceify(dat, "group"))
320+
if (!has_color && !has_symbol && !has_group) x$data <- c(x$data, list(dat))
318321
} else {
319-
dats <- c(dats, list(d))
320-
}
321-
}
322-
x <- list(data = dats)
323-
# carry over properties/data from first trace (if appropriate)
324-
if (length(x$data) > 1 && isTRUE(l$data[[1]]$inherit)) {
325-
for (i in seq.int(2, length(x$data))) {
326-
x$data[[i]] <- modifyList(x$data[[1]], x$data[[i]])
322+
x$data <- c(x$data, list(d))
327323
}
328324
}
329325
# layout() tacks on an unnamed list element to potentially pre-existing
@@ -334,24 +330,19 @@ plotly_build <- function(l = last_plot()) {
334330
# TODO: does this always preserve the correct order to layouts?
335331
# (important since we use modifyList at a later point)
336332
l$layout <- c(list(l$layout[!idx]), l$layout[idx])
333+
} else {
334+
l$layout <- list(l$layout)
337335
}
338336
for (i in seq_along(l$layout)) {
339-
layout <- l$layout[[i]]
340-
idx <- names(layout) %in% c("args", "env", "enclos")
341-
x$layout[[i]] <- if (sum(idx) == 3) {
342-
c(layout[!idx], eval(layout$args, as.list(layout$env, all.names = TRUE), layout$enclos))
343-
} else {
344-
layout
345-
}
337+
x$layout[[i]] <- perform_eval(l$layout[[i]])
346338
}
347339
x$layout <- Reduce(modifyList, x$layout)
348340
# if style is not null, use it to modify existing traces
349341
if (!is.null(l$style)) {
350342
for (i in seq_along(l$style)) {
351-
sty <- l$style[[i]]
352-
idx <- names(sty) %in% c("args", "env", "enclos")
353-
new_sty <- if (sum(idx) == 3) c(sty[!idx], eval(sty$args, as.list(sty$env, all.names = TRUE), sty$enclos)) else sty
354-
for (k in sty$traces) x$data[[k]] <- modifyList(x$data[[k]], new_sty)
343+
sty <- perform_eval(l$style[[i]])
344+
for (k in l$style[[i]]$traces)
345+
x$data[[k]] <- modifyList(x$data[[k]], sty)
355346
}
356347
}
357348
# add appropriate axis title (if they don't already exist)

R/utils.R

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
#' @importFrom stats setNames
44

55
is.plotly <- function(x) inherits(x, "plotly")
6-
6+
77
"%||%" <- function(x, y) {
88
if (length(x) > 0) x else y
99
}
@@ -190,6 +190,25 @@ plotly_headers <- function(type = "main") {
190190
httr::add_headers(.headers = h)
191191
}
192192

193+
194+
perform_eval <- function(x) {
195+
if (should_eval(x)) do_eval(x) else x
196+
}
197+
198+
# env/enclos are special properties specific to the R API
199+
# if they appear _and_ are environments, then evaluate arguments
200+
# (sometimes figures return these properties but evaluation doesn't make sense)
201+
should_eval <- function(x) {
202+
any(vapply(x[c("env", "enclos")], is.environment, logical(1)))
203+
}
204+
205+
# perform evaluation of arguments, keeping other list elements
206+
do_eval <- function(x) {
207+
y <- c(x, eval(x$args, as.list(x$env, all.names = TRUE), x$enclos))
208+
y[c("args", "env", "enclos")] <- NULL
209+
y
210+
}
211+
193212
# try to write environment variables to an .Rprofile
194213
cat_profile <- function(key, value, path = "~") {
195214
r_profile <- file.path(normalizePath(path, mustWork = TRUE),

0 commit comments

Comments
 (0)