65
65
# '
66
66
plot_ly <- function (data = data.frame (), ... , type = " scatter" ,
67
67
group , color , colors , symbol , symbols , size ,
68
- width = NULL , height = NULL , inherit = TRUE ,
68
+ width = NULL , height = NULL , inherit = FALSE ,
69
69
evaluate = FALSE ) {
70
70
# "native" plotly arguments
71
71
argz <- substitute(list (... ))
@@ -170,7 +170,7 @@ layout <- function(p = last_plot(), ...,
170
170
enclos = parent.frame()
171
171
)
172
172
p <- last_plot(p )
173
- p $ layout <- c(p $ layout , list (layout ))
173
+ p $ layout <- c(p $ layout , list (layout = layout ))
174
174
if (evaluate ) p <- plotly_build(p )
175
175
hash_plot(data , p )
176
176
}
@@ -265,9 +265,6 @@ plotly_build <- function(l = last_plot()) {
265
265
# ggplot objects don't need any special type of handling
266
266
if (is.ggplot(l )) return (gg2list(l ))
267
267
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 ))))
271
268
# assume unnamed list elements are data/traces
272
269
nms <- names(l )
273
270
idx <- nms %in% " "
@@ -276,14 +273,22 @@ plotly_build <- function(l = last_plot()) {
276
273
} else if (any(idx )) {
277
274
c(data = c(l $ data , l [idx ]), l [! idx ])
278
275
} else l
279
- dats <- list ()
276
+ # carry over properties, if necessary (but don't carry over evaluation envir)
277
+ if (length(l $ data ) > 1 && isTRUE(l $ data [[1 ]]$ inherit )) {
278
+ d <- l $ data [[1 ]]
279
+ d <- d [! names(d ) %in% c(" env" , " enclos" )]
280
+ for (i in seq.int(2 , length(l $ data ))) {
281
+ l $ data [[i ]] <- modifyList(l $ data [[i ]], d )
282
+ }
283
+ }
284
+ # 'x' is the same as 'l', but with arguments evaluated
285
+ # this is ugly, but I think it is necessary, since we don't know how many
286
+ # traces we have until we evaluate args and call traceify() (or similar)
287
+ x <- list ()
280
288
for (i in seq_along(l $ data )) {
281
289
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
290
+ if (should_eval(d )) {
291
+ dat <- do_eval(d )
287
292
# start processing specially named arguments
288
293
s <- dat [[" size" ]]
289
294
if (! is.null(s )) {
@@ -309,49 +314,30 @@ plotly_build <- function(l = last_plot()) {
309
314
has_group <- ! is.null(dat [[" group" ]])
310
315
if (has_color ) {
311
316
title <- as.list(d $ args )[[" color" ]] %|| % as.list(d $ args )[[" z" ]] %|| % " "
312
- dats <- c(dats , colorize(dat , title ))
317
+ x $ data <- c(x $ data , colorize(dat , title ))
313
318
}
314
319
# 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 ))
320
+ if (has_symbol ) x $ data <- c(x $ data , symbolize(dat ))
321
+ if (has_group ) x $ data <- c(x $ data , traceify(dat , " group" ))
322
+ if (! has_color && ! has_symbol && ! has_group ) x $ data <- c(x $ data , list (dat ))
318
323
} 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 ]])
324
+ x $ data <- c(x $ data , list (d ))
327
325
}
328
326
}
329
- # layout() tacks on an unnamed list element to potentially pre-existing
330
- # layout(s). Note that ggplotly() will return a named list
331
- # of length n >= 1 (so we need to carefully merge them ).
327
+ # it's possible have nested layouts (e.g., plot_ly() %>% layout() %>% layout())
332
328
nms <- names(l $ layout )
333
- if (! is.null(nms ) && any(idx <- nms %in% " " )) {
334
- # TODO: does this always preserve the correct order to layouts?
335
- # (important since we use modifyList at a later point)
336
- l $ layout <- c(list (l $ layout [! idx ]), l $ layout [idx ])
337
- }
329
+ idx <- nms %in% " layout"
330
+ l $ layout <- c(list (l $ layout [! idx ]), setNames(l $ layout [idx ], NULL ))
338
331
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
- }
332
+ x $ layout [[i ]] <- perform_eval(l $ layout [[i ]])
346
333
}
347
334
x $ layout <- Reduce(modifyList , x $ layout )
348
335
# if style is not null, use it to modify existing traces
349
336
if (! is.null(l $ style )) {
350
337
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 )
338
+ sty <- perform_eval(l $ style [[i ]])
339
+ for (k in l $ style [[i ]]$ traces )
340
+ x $ data [[k ]] <- modifyList(x $ data [[k ]], sty )
355
341
}
356
342
}
357
343
# add appropriate axis title (if they don't already exist)
@@ -365,26 +351,6 @@ plotly_build <- function(l = last_plot()) {
365
351
if (! is.null(a ) && ! is.null(names(a ))) {
366
352
x $ layout $ annotations <- list (x $ layout $ annotations )
367
353
}
368
- # search for keyword args in traces and place them at the top level
369
- kwargs <- lapply(x $ data , function (z ) z [get_kwargs()])
370
- # 'top-level' keywords args take precedence
371
- kwargs <- Reduce(modifyList , c(kwargs , list (x [get_kwargs()])))
372
- # empty keyword arguments can cause problems
373
- kwargs <- kwargs [sapply(kwargs , length ) > 0 ]
374
- # try our damndest to assign a sensible filename
375
- if (is.null(kwargs $ filename )) {
376
- kwargs $ filename <-
377
- as.character(kwargs $ layout $ title ) %|| %
378
- paste(
379
- c(kwargs $ layout $ xaxis $ title ,
380
- kwargs $ layout $ yaxis $ title ,
381
- kwargs $ layout $ zaxis $ title ),
382
- collapse = " vs. "
383
- ) %|| %
384
- " plot from api"
385
- }
386
- # tack on keyword arguments
387
- x <- c(x , kwargs )
388
354
# traces shouldn't have any names
389
355
x $ data <- setNames(x $ data , NULL )
390
356
# add plotly class mainly for printing method
0 commit comments