From 481abd90f9f10b633e7fb236b86aeebaf1fd9004 Mon Sep 17 00:00:00 2001 From: "Anonymous@example.com" <cpsievert1@gmail.com> Date: Mon, 16 Feb 2015 12:05:56 -0600 Subject: [PATCH 1/9] Ignore RStudio projects --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index e2325cfa68..589e7d2963 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,5 @@ Rapp.history .Rhistory .RData Makefile +.Rproj.user +*.Rproj From c1742df802f04f3d66b9e61b47dada35293a715b Mon Sep 17 00:00:00 2001 From: "Anonymous@example.com" <cpsievert1@gmail.com> Date: Tue, 17 Feb 2015 18:12:37 -0600 Subject: [PATCH 2/9] update docs --- NAMESPACE | 2 +- man/ensure_file_exist.Rd | 15 +++++++++++++++ man/get_config_file.Rd | 23 +++++++++++++++++++++++ man/get_credentials_file.Rd | 23 +++++++++++++++++++++++ man/gg2list.Rd | 3 ++- man/ggplot_build2.Rd | 3 ++- man/group2NA.Rd | 3 ++- man/layer2traces.Rd | 3 ++- man/paramORdefault.Rd | 3 ++- man/plotly-package.Rd | 5 +++-- man/plotly.Rd | 3 ++- man/set_config_file.Rd | 3 ++- man/set_credentials_file.Rd | 3 ++- man/show_config_file.Rd | 3 ++- man/show_credentials_file.Rd | 3 ++- man/signup.Rd | 3 ++- man/toFill.Rd | 7 +++++-- man/toRGB.Rd | 5 +++-- 18 files changed, 95 insertions(+), 18 deletions(-) create mode 100644 man/ensure_file_exist.Rd create mode 100644 man/get_config_file.Rd create mode 100644 man/get_credentials_file.Rd diff --git a/NAMESPACE b/NAMESPACE index dc6b9f52c4..50bca0d434 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,4 +1,4 @@ -# Generated by roxygen2 (4.0.2): do not edit by hand +# Generated by roxygen2 (4.1.0): do not edit by hand export(gg2list) export(ggplot_build2) diff --git a/man/ensure_file_exist.Rd b/man/ensure_file_exist.Rd new file mode 100644 index 0000000000..ab1c577a2f --- /dev/null +++ b/man/ensure_file_exist.Rd @@ -0,0 +1,15 @@ +% Generated by roxygen2 (4.1.0): do not edit by hand +% Please edit documentation in R/tools.R +\name{ensure_file_exist} +\alias{ensure_file_exist} +\title{Create file if nonexistent} +\usage{ +ensure_file_exist(abspath) +} +\arguments{ +\item{abspath}{Character vector of file path} +} +\description{ +Create file if nonexistent +} + diff --git a/man/get_config_file.Rd b/man/get_config_file.Rd new file mode 100644 index 0000000000..55de20aa9e --- /dev/null +++ b/man/get_config_file.Rd @@ -0,0 +1,23 @@ +% Generated by roxygen2 (4.1.0): do not edit by hand +% Please edit documentation in R/tools.R +\name{get_config_file} +\alias{get_config_file} +\title{Read Plotly config file (which is a JSON) and create one if nonexistent} +\usage{ +get_config_file(args = c()) +} +\arguments{ +\item{args}{Character vector of keys you are looking up} +} +\value{ +List of keyword-value pairs (config) +} +\description{ +Read Plotly config file (which is a JSON) and create one if nonexistent +} +\examples{ +\dontrun{ +get_config_file(c("plotly_domain", "plotly_streaming_domain")) +} +} + diff --git a/man/get_credentials_file.Rd b/man/get_credentials_file.Rd new file mode 100644 index 0000000000..4c3178d002 --- /dev/null +++ b/man/get_credentials_file.Rd @@ -0,0 +1,23 @@ +% Generated by roxygen2 (4.1.0): do not edit by hand +% Please edit documentation in R/tools.R +\name{get_credentials_file} +\alias{get_credentials_file} +\title{Read Plotly credentials file (which is a JSON)} +\usage{ +get_credentials_file(args = c()) +} +\arguments{ +\item{args}{Character vector of keys you are looking up} +} +\value{ +List of keyword-value pairs (credentials) +} +\description{ +Read Plotly credentials file (which is a JSON) +} +\examples{ +\dontrun{ +get_credentials_file(c("username", "api_key")) +} +} + diff --git a/man/gg2list.Rd b/man/gg2list.Rd index c3080229a6..67016d2a0d 100644 --- a/man/gg2list.Rd +++ b/man/gg2list.Rd @@ -1,4 +1,5 @@ -% Generated by roxygen2 (4.0.2): do not edit by hand +% Generated by roxygen2 (4.1.0): do not edit by hand +% Please edit documentation in R/ggplotly.R \name{gg2list} \alias{gg2list} \title{Convert a ggplot to a list.} diff --git a/man/ggplot_build2.Rd b/man/ggplot_build2.Rd index 5125cfff1d..a37efa75c4 100644 --- a/man/ggplot_build2.Rd +++ b/man/ggplot_build2.Rd @@ -1,4 +1,5 @@ -% Generated by roxygen2 (4.0.2): do not edit by hand +% Generated by roxygen2 (4.1.0): do not edit by hand +% Please edit documentation in R/build_function.R \name{ggplot_build2} \alias{ggplot_build2} \title{ggplot build function with enhanced return} diff --git a/man/group2NA.Rd b/man/group2NA.Rd index faeb97f3f8..c1114f4c35 100644 --- a/man/group2NA.Rd +++ b/man/group2NA.Rd @@ -1,4 +1,5 @@ -% Generated by roxygen2 (4.0.2): do not edit by hand +% Generated by roxygen2 (4.1.0): do not edit by hand +% Please edit documentation in R/trace_generation.R \name{group2NA} \alias{group2NA} \title{Drawing ggplot2 geoms with a group aesthetic is most efficient in diff --git a/man/layer2traces.Rd b/man/layer2traces.Rd index b0e80b9bf0..d57083fe5b 100644 --- a/man/layer2traces.Rd +++ b/man/layer2traces.Rd @@ -1,4 +1,5 @@ -% Generated by roxygen2 (4.0.2): do not edit by hand +% Generated by roxygen2 (4.1.0): do not edit by hand +% Please edit documentation in R/trace_generation.R \name{layer2traces} \alias{layer2traces} \title{Convert a layer to a list of traces. Called from gg2list()} diff --git a/man/paramORdefault.Rd b/man/paramORdefault.Rd index 491d3cbcd9..c0cb1cb457 100644 --- a/man/paramORdefault.Rd +++ b/man/paramORdefault.Rd @@ -1,4 +1,5 @@ -% Generated by roxygen2 (4.0.2): do not edit by hand +% Generated by roxygen2 (4.1.0): do not edit by hand +% Please edit documentation in R/marker_conversion.R \name{paramORdefault} \alias{paramORdefault} \title{Convert ggplot params to plotly.} diff --git a/man/plotly-package.Rd b/man/plotly-package.Rd index 91ca574ac1..2643bf2dd2 100644 --- a/man/plotly-package.Rd +++ b/man/plotly-package.Rd @@ -1,4 +1,5 @@ -% Generated by roxygen2 (4.0.2): do not edit by hand +% Generated by roxygen2 (4.1.0): do not edit by hand +% Please edit documentation in R/plotly-package.r \docType{package} \name{plotly-package} \alias{plotly-package} @@ -14,7 +15,7 @@ An example of an interactive graph made from the R API: https://plot.ly/~chris/4 \itemize{ \item Package: plotly \item Type: Package - \item Version: 0.3.4 + \item Version: 0.5.20 \item Date: 2014-03-07 \item License: MIT } diff --git a/man/plotly.Rd b/man/plotly.Rd index 6119c5a751..0c4db5bd35 100644 --- a/man/plotly.Rd +++ b/man/plotly.Rd @@ -1,4 +1,5 @@ -% Generated by roxygen2 (4.0.2): do not edit by hand +% Generated by roxygen2 (4.1.0): do not edit by hand +% Please edit documentation in R/plotly.R \name{plotly} \alias{plotly} \title{Main interface to plotly} diff --git a/man/set_config_file.Rd b/man/set_config_file.Rd index 59fde04bbf..d37aaf672b 100644 --- a/man/set_config_file.Rd +++ b/man/set_config_file.Rd @@ -1,4 +1,5 @@ -% Generated by roxygen2 (4.0.2): do not edit by hand +% Generated by roxygen2 (4.1.0): do not edit by hand +% Please edit documentation in R/tools.R \name{set_config_file} \alias{set_config_file} \title{Set keyword-value pairs in Plotly config file} diff --git a/man/set_credentials_file.Rd b/man/set_credentials_file.Rd index 3f398d1e18..b5145446c8 100644 --- a/man/set_credentials_file.Rd +++ b/man/set_credentials_file.Rd @@ -1,4 +1,5 @@ -% Generated by roxygen2 (4.0.2): do not edit by hand +% Generated by roxygen2 (4.1.0): do not edit by hand +% Please edit documentation in R/tools.R \name{set_credentials_file} \alias{set_credentials_file} \title{Set the keyword-value pairs in Plotly credentials file} diff --git a/man/show_config_file.Rd b/man/show_config_file.Rd index 178dbab4a9..1f781075f7 100644 --- a/man/show_config_file.Rd +++ b/man/show_config_file.Rd @@ -1,4 +1,5 @@ -% Generated by roxygen2 (4.0.2): do not edit by hand +% Generated by roxygen2 (4.1.0): do not edit by hand +% Please edit documentation in R/tools.R \name{show_config_file} \alias{show_config_file} \title{Read and print Plotly config file, wrapping get_credentials_file()} diff --git a/man/show_credentials_file.Rd b/man/show_credentials_file.Rd index e4c2072311..70a9bda657 100644 --- a/man/show_credentials_file.Rd +++ b/man/show_credentials_file.Rd @@ -1,4 +1,5 @@ -% Generated by roxygen2 (4.0.2): do not edit by hand +% Generated by roxygen2 (4.1.0): do not edit by hand +% Please edit documentation in R/tools.R \name{show_credentials_file} \alias{show_credentials_file} \title{Read and print Plotly credentials file, wrapping get_credentials_file()} diff --git a/man/signup.Rd b/man/signup.Rd index 226094019d..577d1d1c5d 100644 --- a/man/signup.Rd +++ b/man/signup.Rd @@ -1,4 +1,5 @@ -% Generated by roxygen2 (4.0.2): do not edit by hand +% Generated by roxygen2 (4.1.0): do not edit by hand +% Please edit documentation in R/signup.R \name{signup} \alias{signup} \title{Sign up to plotly.} diff --git a/man/toFill.Rd b/man/toFill.Rd index abc71b77ec..05e3f49dff 100644 --- a/man/toFill.Rd +++ b/man/toFill.Rd @@ -1,12 +1,15 @@ -% Generated by roxygen2 (4.0.2): do not edit by hand +% Generated by roxygen2 (4.1.0): do not edit by hand +% Please edit documentation in R/colour_conversion.R \name{toFill} \alias{toFill} \title{Use default ggplot colour for fill (gray20) if not declared} \usage{ -toFill(x) +toFill(x, alpha = 1) } \arguments{ \item{x}{character for colour} + +\item{alpha}{transparency alpha} } \value{ hexadecimal colour value diff --git a/man/toRGB.Rd b/man/toRGB.Rd index c0cac1536c..5e75af9c0b 100644 --- a/man/toRGB.Rd +++ b/man/toRGB.Rd @@ -1,4 +1,5 @@ -% Generated by roxygen2 (4.0.2): do not edit by hand +% Generated by roxygen2 (4.1.0): do not edit by hand +% Please edit documentation in R/colour_conversion.R \name{toRGB} \alias{toRGB} \title{Convert R colours to RGBA hexadecimal colour values} @@ -8,7 +9,7 @@ toRGB(x, alpha = 1) \arguments{ \item{x}{character for colour, for example: "white"} -\item{alpha}{alpha} +\item{alpha}{transparency alpha} } \value{ hexadecimal colour value (if is.na(x), return "transparent" for compatibility with Plotly) From 12f471a3e642eb112cfacbfcd988c2cab33a0a0f Mon Sep 17 00:00:00 2001 From: "Anonymous@example.com" <cpsievert1@gmail.com> Date: Tue, 17 Feb 2015 18:14:53 -0600 Subject: [PATCH 3/9] Implement free scales for facet_wrap. Fixes #166 --- R/ggplotly.R | 314 +++++++++++++++++++---------------- tests/testthat/test-facets.R | 46 +++++ 2 files changed, 217 insertions(+), 143 deletions(-) diff --git a/R/ggplotly.R b/R/ggplotly.R index 868122718f..cf26cf407f 100644 --- a/R/ggplotly.R +++ b/R/ggplotly.R @@ -30,19 +30,19 @@ polygon.line.defaults$colour <- NA lty2dash <- c(numeric.lty, named.lty, coded.lty) aesConverters <- list(linetype=function(lty) { - lty2dash[as.character(lty)] - }, - colour=function(col) { - toRGB(col) - }, - size=identity, - sizeref=identity, - sizemode=identity, - alpha=identity, - shape=function(pch) { - pch2symbol[as.character(pch)] - }, - direction=identity) + lty2dash[as.character(lty)] +}, +colour=function(col) { + toRGB(col) +}, +size=identity, +sizeref=identity, +sizemode=identity, +alpha=identity, +shape=function(pch) { + pch2symbol[as.character(pch)] +}, +direction=identity) markLegends <- ## NOTE: Do we also want to split on size? @@ -168,6 +168,9 @@ gg2list <- function(p){ gglayout <- built$panel$layout ## invert rows so that plotly and ggplot2 show panels in the same order gglayout$plotly.row <- max(gglayout$ROW) - gglayout$ROW + 1 + ## ugh, ggplot counts panel right-to-left & top-to-bottom + ## plotly count them right-to-left & *bottom-to-top* + gglayout$plotly.panel <- with(gglayout, order(plotly.row, COL)) ## Add ROW and COL to df: needed to link axes to traces; keep df's ## original ordering while merging. @@ -175,7 +178,7 @@ gg2list <- function(p){ df <- merge(df, gglayout[, c("PANEL", "plotly.row", "COL")]) df <- df[order(df$order),] df$order <- NULL - + misc$prestats.data <- merge(built$prestats.data[[i]], gglayout[, c("PANEL", "plotly.row", "COL")]) @@ -191,7 +194,7 @@ gg2list <- function(p){ misc$prestats.data$globsizemin <- ggsizemin misc$prestats.data$globsizemax <- ggsizemax } - + ## This extracts essential info for this geom/layer. traces <- layer2traces(L, df, misc) @@ -219,7 +222,7 @@ gg2list <- function(p){ trace.list <- c(trace.list, traces) } } - + ## for barcharts, verify that all traces have the same barmode; we don't ## support different barmodes on the same plot yet. barmodes <- do.call(c, lapply(trace.list, function (x) x$barmode)) @@ -278,12 +281,12 @@ gg2list <- function(p){ grid <- theme.pars$panel.grid grid.major <- theme.pars$panel.grid.major if ((!is.null(grid$linetype) || !is.null(grid.major$linetype)) && - c(grid$linetype, grid.major$linetype) %in% c(2, 3, "dashed", "dotted")) { + c(grid$linetype, grid.major$linetype) %in% c(2, 3, "dashed", "dotted")) { ax.list$gridcolor <- ifelse(is.null(grid.major$colour), - toRGB(grid$colour, 0.1), - toRGB(grid.major$colour, 0.1)) + toRGB(grid$colour, 0.1), + toRGB(grid.major$colour, 0.1)) } else { - ax.list$gridcolor <- toRGB(grid.major$colour) + ax.list$gridcolor <- toRGB(grid.major$colour) } ax.list$showgrid <- !is.blank(s("panel.grid.major.%s")) @@ -349,7 +352,6 @@ gg2list <- function(p){ !is.blank(s("axis.line.%s")) layout[[s("%saxis")]] <- ax.list } - ## copy [x/y]axis to [x/y]axisN and set domain, range, etc. for each xaxis.title <- layout$xaxis$title yaxis.title <- layout$yaxis$title @@ -357,130 +359,156 @@ gg2list <- function(p){ outer.margin <- 0.05 ## to put titles outside of the plots orig.xaxis <- layout$xaxis orig.yaxis <- layout$yaxis - if (nrow(gglayout) > 1) - { - row.size <- 1. / max(gglayout$ROW) - col.size <- 1. / max(gglayout$COL) - for (i in seq_len(nrow(gglayout))) - { - row <- gglayout[i, "plotly.row"] - col <- gglayout[i, "COL"] - x <- col * col.size - xmin <- x - col.size - xmax <- x - inner.margin - y <- row * row.size - ymin <- y - row.size - ymax <- y - inner.margin - if ("wrap" %in% class(p$facet)) - ymax <- ymax - 0.04 - yaxis.name <- if (row == 1) "yaxis" else paste0("yaxis", row) - xaxis.name <- if (col == 1) "xaxis" else paste0("xaxis", col) - layout[[xaxis.name]] <- orig.xaxis - layout[[xaxis.name]]$domain <- c(xmin, xmax) - layout[[xaxis.name]]$anchor <- "y" - layout[[xaxis.name]]$title <- NULL - if (orig.xaxis$type == "linear" && # range only makes sense for numeric data - (is.null(p$facet$scales) || p$facet$scales == "fixed" || p$facet$scales == "free_y")) - { - layout[[xaxis.name]]$range <- built$panel$ranges[[i]]$x.range - layout[[xaxis.name]]$autorange <- FALSE - } - - layout[[yaxis.name]] <- orig.yaxis - layout[[yaxis.name]]$domain <- c(ymin, ymax) - layout[[yaxis.name]]$anchor <- "x" - layout[[yaxis.name]]$title <- NULL - if (orig.yaxis$type == "linear" && # range only makes sense for numeric data - (is.null(p$facet$scales) || p$facet$scales == "fixed" || p$facet$scales == "free_x")) - { - layout[[yaxis.name]]$range <- built$panel$ranges[[i]]$y.range - layout[[yaxis.name]]$autorange <- FALSE - } - + if (nrow(gglayout) > 1) { + row.size <- 1. / max(gglayout$ROW) + col.size <- 1. / max(gglayout$COL) + npanels <- nrow(gglayout) + for (i in seq_len(npanels)) { + row <- gglayout[i, "plotly.row"] + col <- gglayout[i, "COL"] + panel <- gglayout[i, "plotly.panel"] + x <- col * col.size + xmin <- x - col.size + xmax <- x - inner.margin + y <- row * row.size + ymin <- y - row.size + ymax <- y - inner.margin + xaxis.name <- if (panel == 1) "xaxis" else paste0("xaxis", panel) + yaxis.name <- if (panel == 1) "yaxis" else paste0("yaxis", panel) + #layout defaults that won't change depending on the type of facet + layout[[xaxis.name]] <- orig.xaxis + layout[[xaxis.name]]$domain <- c(xmin, xmax) + layout[[xaxis.name]]$title <- NULL + layout[[yaxis.name]] <- orig.yaxis + layout[[yaxis.name]]$domain <- c(ymin, ymax) + layout[[yaxis.name]]$title <- NULL + # facet_wrap allows for a different x/y axis on each panel + if ("wrap" %in% class(p$facet)) { + # make room for facet strip label + ymax <- ymax - 0.04 + # make room for yaxis labels (this should be a function of label size) + if (col == 1) { + xmax <- xmax - 0.02 + } else { + xmin <- xmin + 0.02 } - ## add panel titles as annotations - annotations <- list() + # make room for xaxis labels + if (row == 1) { + ymax <- ymax - 0.02 + } else { + ymin <- ymin + 0.02 + } + if (p$facet$free$y && panel > 1) { + # is it safe to assume npanels == ntraces? + yaxis.name <- paste0("yaxis", panel) + trace.list[[i]]$yaxis <- paste0("y", panel) + layout[[yaxis.name]]$anchor <- paste0("x", panel) + } + if (p$facet$free$x && panel > 1) { + xaxis.name <- paste0("xaxis", panel) + trace.list[[i]]$xaxis <- paste0("x", panel) + layout[[xaxis.name]]$anchor <- paste0("y", panel) + } + layout[[xaxis.name]]$domain <- c(xmin, xmax) + layout[[yaxis.name]]$domain <- c(ymin, ymax) + } + if (is.null(layout[[xaxis.name]]$anchor)) + layout[[xaxis.name]]$anchor <- "y" + if (is.null(layout[[yaxis.name]]$anchor)) + layout[[yaxis.name]]$anchor <- "x" + # range only makes sense for numeric data + if (orig.xaxis$type == "linear") { + layout[[xaxis.name]]$range <- built$panel$ranges[[i]]$x.range + layout[[xaxis.name]]$autorange <- FALSE + } + if (orig.yaxis$type == "linear") { + layout[[yaxis.name]]$range <- built$panel$ranges[[i]]$y.range + layout[[yaxis.name]]$autorange <- FALSE + } + } + ## add panel titles as annotations + annotations <- list() + nann <- 1 + make.label <- function(text, x, y, xanchor="auto", yanchor="auto", textangle=0) + list(text=text, showarrow=FALSE, x=x, y=y, ax=0, ay=0, + xref="paper", yref="paper", xanchor=xanchor, yanchor=yanchor, + textangle=textangle) + + if ("grid" %in% class(p$facet)) + { + frows <- names(p$facet$rows) nann <- 1 - make.label <- function(text, x, y, xanchor="auto", yanchor="auto", textangle=0) - list(text=text, showarrow=FALSE, x=x, y=y, ax=0, ay=0, - xref="paper", yref="paper", xanchor=xanchor, yanchor=yanchor, - textangle=textangle) - if ("grid" %in% class(p$facet)) - { - frows <- names(p$facet$rows) - nann <- 1 - - for (i in seq_len(max(gglayout$ROW))) - { - text <- paste(lapply(gglayout[gglayout$ROW == i, frows, drop=FALSE][1,], - as.character), - collapse=", ") - if (text != "") { # to not create extra annotations - increase_margin_r <- TRUE - annotations[[nann]] <- make.label(text, - 1 + outer.margin - 0.04, - row.size * (max(gglayout$ROW)-i+0.5), - xanchor="center", - textangle=90) - nann <- nann + 1 - } - } - - fcols <- names(p$facet$cols) - for (i in seq_len(max(gglayout$COL))) - { - text <- paste(lapply(gglayout[gglayout$COL == i, fcols, drop=FALSE][1,], - as.character), - collapse=", ") - if (text!="") { - annotations[[nann]] <- make.label(text, - col.size * (i-0.5) - inner.margin/2, - 1 + outer.margin, - xanchor="center") - nann <- nann + 1 - } - } - - ## add empty traces everywhere so that the background shows even if there - ## is no data for a facet - for (r in seq_len(max(gglayout$ROW))) - for (c in seq_len(max(gglayout$COL))) - trace.list <- c(trace.list, list(list(xaxis=paste0("x", c), yaxis=paste0("y", r), showlegend=FALSE))) + for (i in seq_len(max(gglayout$ROW))) + { + text <- paste(lapply(gglayout[gglayout$ROW == i, frows, drop=FALSE][1,], + as.character), + collapse=", ") + if (text != "") { # to not create extra annotations + increase_margin_r <- TRUE + annotations[[nann]] <- make.label(text, + 1 + outer.margin - 0.04, + row.size * (max(gglayout$ROW)-i+0.5), + xanchor="center", + textangle=90) + nann <- nann + 1 } - else if ("wrap" %in% class(p$facet)) - { - facets <- names(p$facet$facets) - for (i in seq_len(max(as.numeric(gglayout$PANEL)))) - { - ix <- gglayout$PANEL == i - row <- gglayout$ROW[ix] - col <- gglayout$COL[ix] - text <- paste(lapply(gglayout[ix, facets, drop=FALSE][1,], - as.character), - collapse=", ") - annotations[[nann]] <- make.label(text, - col.size * (col-0.5) - inner.margin/2, - row.size * (max(gglayout$ROW) - row + 0.985), - xanchor="center", - yanchor="top") - nann <- nann + 1 - } - } - - ## axes titles - annotations[[nann]] <- make.label(xaxis.title, - 0.5, - -outer.margin, - yanchor="top") - nann <- nann + 1 - annotations[[nann]] <- make.label(yaxis.title, - -outer.margin, - 0.5, - textangle=-90) + } - layout$annotations <- annotations + fcols <- names(p$facet$cols) + for (i in seq_len(max(gglayout$COL))) + { + text <- paste(lapply(gglayout[gglayout$COL == i, fcols, drop=FALSE][1,], + as.character), + collapse=", ") + if (text!="") { + annotations[[nann]] <- make.label(text, + col.size * (i-0.5) - inner.margin/2, + 1 + outer.margin, + xanchor="center") + nann <- nann + 1 + } + } + + ## add empty traces everywhere so that the background shows even if there + ## is no data for a facet + for (r in seq_len(max(gglayout$ROW))) + for (c in seq_len(max(gglayout$COL))) + trace.list <- c(trace.list, list(list(xaxis=paste0("x", c), yaxis=paste0("y", r), showlegend=FALSE))) } + else if ("wrap" %in% class(p$facet)) + { + facets <- names(p$facet$facets) + for (i in seq_len(max(as.numeric(gglayout$PANEL)))) + { + ix <- gglayout$PANEL == i + row <- gglayout$ROW[ix] + col <- gglayout$COL[ix] + text <- paste(lapply(gglayout[ix, facets, drop=FALSE][1,], + as.character), + collapse=", ") + annotations[[nann]] <- make.label(text, + col.size * (col-0.5) - inner.margin/2, + row.size * (max(gglayout$ROW) - row + 0.985), + xanchor="center", + yanchor="top") + nann <- nann + 1 + } + } + + ## axes titles + annotations[[nann]] <- make.label(xaxis.title, + 0.5, + -outer.margin, + yanchor="top") + nann <- nann + 1 + annotations[[nann]] <- make.label(yaxis.title, + -outer.margin, + 0.5, + textangle=-90) + + layout$annotations <- annotations + } ## Remove legend if theme has no legend position layout$showlegend <- !(theme.pars$legend.position=="none") @@ -505,7 +533,7 @@ gg2list <- function(p){ # [markUnique != "x"] is for boxplot's particular case. if (any(names(layer.aes) %in% markUnique[markUnique != "x"]) == FALSE) layout$showlegend <- FALSE - + if (layout$showlegend && length(p$data)) { # Retrieve legend title legend.elements <- sapply(traces, "[[", "name") @@ -515,7 +543,7 @@ gg2list <- function(p){ legend.title <- colnames(p$data)[i] } legend.title <- paste0("<b>", legend.title, "</b>") - + # Create legend title element as an annotation if (exists("annotations")) { nann <- nann + 1 @@ -532,7 +560,7 @@ gg2list <- function(p){ textangle=0) layout$annotations <- annotations } - + ## Family font for text if (!is.null(theme.pars$text$family)) { layout$titlefont$family <- theme.pars$text$family @@ -601,7 +629,7 @@ gg2list <- function(p){ } } } - + # If background elements are NULL, and background rect (rectangle) is defined: rect_fill <- theme.pars$rect$fill if (!is.null(rect_fill)) { diff --git a/tests/testthat/test-facets.R b/tests/testthat/test-facets.R index 5ea8414484..a52348bff9 100644 --- a/tests/testthat/test-facets.R +++ b/tests/testthat/test-facets.R @@ -47,3 +47,49 @@ test_that("3 facets becomes 3 panels", { u <- unique(trace.axes.df) expect_identical(nrow(u), 3L) }) + +# expect a certain number of _unique_ [x/y] axes +expect_axes <- function(info, n, axis = "x") { + pattern <- paste0("^", axis, "axis([0-9]+)?$") + #n.axes <- length(grep(pattern, names(info$kwargs$layout))) + axes <- with(info$kwargs, layout[grepl(pattern, names(layout))]) + n.axes <- length(axes) + ranges <- do.call("rbind", lapply(axes, function(x) x$range)) + expect_identical(nrow(unique(ranges)), as.integer(n)) +} + +no_panels <- ggplot(mtcars, aes(mpg, wt)) + geom_point() + +test_that("facet_wrap(..., scales = 'free') creates interior scales", { + free_both <- no_panels + facet_wrap(~am+vs, scales = "free") + info <- gg2list(free_both) + expect_axes(info, 4L) + expect_axes(info, 4L, "y") + + free_y <- no_panels + facet_wrap(~am+vs, scales = "free_y") + info <- gg2list(free_y) + expect_axes(info, 1L) + expect_axes(info, 4L, "y") + + free_x <- no_panels + facet_wrap(~am+vs, scales = "free_x") + info <- gg2list(free_x) + expect_axes(info, 4L) + expect_axes(info, 1L, "y") +}) + +test_that("facet_grid(..., scales = 'free') doesnt create interior scales.", { + free_both <- no_panels + facet_grid(vs~am, scales = "free") + info <- gg2list(free_both) + expect_axes(info, 2L) + expect_axes(info, 2L, "y") + + free_y <- no_panels + facet_grid(vs~am, scales = "free_y") + info <- gg2list(free_y) + expect_axes(info, 1L) + expect_axes(info, 2L, "y") + + free_x <- no_panels + facet_grid(vs~am, scales = "free_x") + info <- gg2list(free_x) + expect_axes(info, 2L) + expect_axes(info, 1L, "y") +}) \ No newline at end of file From 19e42abde99ef0663e7adcb6a6a70deb6ae1278f Mon Sep 17 00:00:00 2001 From: "Anonymous@example.com" <cpsievert1@gmail.com> Date: Tue, 17 Feb 2015 19:19:35 -0600 Subject: [PATCH 4/9] Fix identation of aesConverters --- R/ggplotly.R | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/R/ggplotly.R b/R/ggplotly.R index cf26cf407f..6fe797c617 100644 --- a/R/ggplotly.R +++ b/R/ggplotly.R @@ -29,20 +29,22 @@ polygon.line.defaults$colour <- NA # Convert R lty line type codes to plotly "dash" codes. lty2dash <- c(numeric.lty, named.lty, coded.lty) -aesConverters <- list(linetype=function(lty) { - lty2dash[as.character(lty)] -}, -colour=function(col) { - toRGB(col) -}, -size=identity, -sizeref=identity, -sizemode=identity, -alpha=identity, -shape=function(pch) { - pch2symbol[as.character(pch)] -}, -direction=identity) +aesConverters <- list( + linetype=function(lty) { + lty2dash[as.character(lty)] + }, + colour=function(col) { + toRGB(col) + }, + size=identity, + sizeref=identity, + sizemode=identity, + alpha=identity, + shape=function(pch) { + pch2symbol[as.character(pch)] + }, + direction=identity +) markLegends <- ## NOTE: Do we also want to split on size? From b7346cd3b04597aa8f7858bff8ef7db5d2564577 Mon Sep 17 00:00:00 2001 From: "Anonymous@example.com" <cpsievert1@gmail.com> Date: Fri, 20 Feb 2015 14:21:40 -0600 Subject: [PATCH 5/9] single pound for comments --- R/ggplotly.R | 142 +++++++++++++++++++++++++-------------------------- 1 file changed, 71 insertions(+), 71 deletions(-) diff --git a/R/ggplotly.R b/R/ggplotly.R index 6fe797c617..2bed029183 100644 --- a/R/ggplotly.R +++ b/R/ggplotly.R @@ -1,4 +1,4 @@ -## calc. the epoch +# calc. the epoch now <- Sys.time() the.epoch <- now - as.numeric(now) @@ -47,9 +47,9 @@ aesConverters <- list( ) markLegends <- - ## NOTE: Do we also want to split on size? - ## Legends based on sizes not implemented yet in Plotly - ## list(point=c("colour", "fill", "shape", "size"), + # NOTE: Do we also want to split on size? + # Legends based on sizes not implemented yet in Plotly + # list(point=c("colour", "fill", "shape", "size"), list(point=c("colour", "fill", "shape"), path=c("linetype", "size", "colour", "shape"), polygon=c("colour", "fill", "linetype", "size", "group"), @@ -70,10 +70,10 @@ gg2list <- function(p){ if(length(p$layers) == 0) { stop("No layers in plot") } - ## Always use identity size scale so that plot.ly gets the real - ## units for the size variables. + # Always use identity size scale so that plot.ly gets the real + # units for the size variables. p <- tryCatch({ - ## this will be an error for discrete variables. + # this will be an error for discrete variables. suppressMessages({ ggplot_build(p+scale_size_continuous()) p+scale_size_identity() @@ -84,10 +84,10 @@ gg2list <- function(p){ layout <- list() trace.list <- list() - ## Before building the ggplot, we would like to add aes(name) to - ## figure out what the object group is later. This also copies any - ## needed global aes/data values to each layer, so we do not have to - ## worry about combining global and layer-specific aes/data later. + # Before building the ggplot, we would like to add aes(name) to + # figure out what the object group is later. This also copies any + # needed global aes/data values to each layer, so we do not have to + # worry about combining global and layer-specific aes/data later. for(layer.i in seq_along(p$layers)) { layer.aes <- p$layers[[layer.i]]$mapping to.copy <- names(p$mapping)[!names(p$mapping) %in% names(layer.aes)] @@ -101,7 +101,7 @@ gg2list <- function(p){ } } - ## Extract data from built ggplots + # Extract data from built ggplots built <- ggplot_build2(p) # Get global x-range now because we need some of its info in layer2traces @@ -123,20 +123,20 @@ gg2list <- function(p){ } for(i in seq_along(built$plot$layers)){ - ## This is the layer from the original ggplot object. + # This is the layer from the original ggplot object. L <- p$layers[[i]] - ## for each layer, there is a correpsonding data.frame which - ## evaluates the aesthetic mapping. + # for each layer, there is a correpsonding data.frame which + # evaluates the aesthetic mapping. df <- built$data[[i]] - ## Test fill and color to see if they encode a quantitative - ## variable. This may be useful for several reasons: (1) it is - ## sometimes possible to plot several different colors in the same - ## trace (e.g. points), and that is faster for large numbers of - ## data points and colors; (2) factors on x or y axes should be - ## sent to plotly as characters, not as numeric data (which is - ## what ggplot_build gives us). + # Test fill and color to see if they encode a quantitative + # variable. This may be useful for several reasons: (1) it is + # sometimes possible to plot several different colors in the same + # trace (e.g. points), and that is faster for large numbers of + # data points and colors; (2) factors on x or y axes should be + # sent to plotly as characters, not as numeric data (which is + # what ggplot_build gives us). misc <- list() for(a in c("fill", "colour", "x", "y")){ for(data.type in c("continuous", "date", "datetime", "discrete")){ @@ -155,7 +155,7 @@ gg2list <- function(p){ } } - ## scales are needed for legend ordering. + # scales are needed for legend ordering. for(sc in p$scales$scales){ a <- sc$aesthetics if(length(a) == 1){ @@ -166,16 +166,16 @@ gg2list <- function(p){ } } - ## get gglayout now because we need some of its info in layer2traces + # get gglayout now because we need some of its info in layer2traces gglayout <- built$panel$layout - ## invert rows so that plotly and ggplot2 show panels in the same order + # invert rows so that plotly and ggplot2 show panels in the same order gglayout$plotly.row <- max(gglayout$ROW) - gglayout$ROW + 1 - ## ugh, ggplot counts panel right-to-left & top-to-bottom - ## plotly count them right-to-left & *bottom-to-top* + # ugh, ggplot counts panel right-to-left & top-to-bottom + # plotly count them right-to-left & *bottom-to-top* gglayout$plotly.panel <- with(gglayout, order(plotly.row, COL)) - ## Add ROW and COL to df: needed to link axes to traces; keep df's - ## original ordering while merging. + # Add ROW and COL to df: needed to link axes to traces; keep df's + # original ordering while merging. df$order <- seq_len(nrow(df)) df <- merge(df, gglayout[, c("PANEL", "plotly.row", "COL")]) df <- df[order(df$order),] @@ -197,7 +197,7 @@ gg2list <- function(p){ misc$prestats.data$globsizemax <- ggsizemax } - ## This extracts essential info for this geom/layer. + # This extracts essential info for this geom/layer. traces <- layer2traces(L, df, misc) # Associate error bars with previous traces @@ -225,8 +225,8 @@ gg2list <- function(p){ } } - ## for barcharts, verify that all traces have the same barmode; we don't - ## support different barmodes on the same plot yet. + # for barcharts, verify that all traces have the same barmode; we don't + # support different barmodes on the same plot yet. barmodes <- do.call(c, lapply(trace.list, function (x) x$barmode)) barmodes <- barmodes[!is.null(barmodes)] if (length(barmodes) > 0) { @@ -250,27 +250,27 @@ gg2list <- function(p){ } } - ## 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. + # 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(p) - ## 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. - ## ranges <- built$panel$ranges[[1]] - ## if("flip"%in%attr(built$plot$coordinates, "class")){ - ## temp <- built$plot$labels$x - ## built$plot$labels$x <- built$plot$labels$y - ## built$plot$labels$y <- temp - ## } + # 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. + # ranges <- built$panel$ranges[[1]] + # if("flip"%in%attr(built$plot$coordinates, "class")){ + # temp <- built$plot$labels$x + # built$plot$labels$x <- built$plot$labels$y + # built$plot$labels$y <- temp + # } e <- function(el.name){ ggplot2::calc_element(el.name, p$theme) } is.blank <- function(el.name, null.is.blank=FALSE) { - ## NULL shows ticks and hides borders + # NULL shows ticks and hides borders cls <- attr(e(el.name),"class") "element_blank" %in% cls || null.is.blank && is.null(cls) } @@ -279,7 +279,7 @@ gg2list <- function(p){ s <- function(tmp)sprintf(tmp, xy) ax.list$tickcolor <- toRGB(theme.pars$axis.ticks$colour) - ## When gridlines are dotted or dashed: + # When gridlines are dotted or dashed: grid <- theme.pars$panel.grid grid.major <- theme.pars$panel.grid.major if ((!is.null(grid$linetype) || !is.null(grid.major$linetype)) && @@ -292,9 +292,9 @@ gg2list <- function(p){ } ax.list$showgrid <- !is.blank(s("panel.grid.major.%s")) - ## These numeric length variables are not easily convertible. - ##ax.list$gridwidth <- as.numeric(theme.pars$panel.grid.major$size) - ##ax.list$ticklen <- as.numeric(theme.pars$axis.ticks.length) + # These numeric length variables are not easily convertible. + #ax.list$gridwidth <- as.numeric(theme.pars$panel.grid.major$size) + #ax.list$ticklen <- as.numeric(theme.pars$axis.ticks.length) theme2font <- function(text){ if(!is.null(text)){ @@ -320,7 +320,7 @@ gg2list <- function(p){ } ax.list$tickfont <- theme2font(tick.text) - ## Translate axes labels. + # Translate axes labels. scale.i <- which(p$scales$find(xy)) ax.list$title <- if(length(scale.i)){ sc <- p$scales$scales[[scale.i]] @@ -349,16 +349,16 @@ gg2list <- function(p){ ax.list$showline <- !is.blank("panel.border", TRUE) ax.list$linecolor <- toRGB(theme.pars$panel.border$colour) ax.list$linewidth <- theme.pars$panel.border$size - ## Some other params that we used in animint but we don't yet - ## translate to plotly: + # Some other params that we used in animint but we don't yet + # translate to plotly: !is.blank(s("axis.line.%s")) layout[[s("%saxis")]] <- ax.list } - ## copy [x/y]axis to [x/y]axisN and set domain, range, etc. for each + # copy [x/y]axis to [x/y]axisN and set domain, range, etc. for each xaxis.title <- layout$xaxis$title yaxis.title <- layout$yaxis$title - inner.margin <- 0.01 ## between facets - outer.margin <- 0.05 ## to put titles outside of the plots + inner.margin <- 0.01 # between facets + outer.margin <- 0.05 # to put titles outside of the plots orig.xaxis <- layout$xaxis orig.yaxis <- layout$yaxis if (nrow(gglayout) > 1) { @@ -428,7 +428,7 @@ gg2list <- function(p){ layout[[yaxis.name]]$autorange <- FALSE } } - ## add panel titles as annotations + # add panel titles as annotations annotations <- list() nann <- 1 make.label <- function(text, x, y, xanchor="auto", yanchor="auto", textangle=0) @@ -472,8 +472,8 @@ gg2list <- function(p){ } } - ## add empty traces everywhere so that the background shows even if there - ## is no data for a facet + # add empty traces everywhere so that the background shows even if there + # is no data for a facet for (r in seq_len(max(gglayout$ROW))) for (c in seq_len(max(gglayout$COL))) trace.list <- c(trace.list, list(list(xaxis=paste0("x", c), yaxis=paste0("y", r), showlegend=FALSE))) @@ -498,7 +498,7 @@ gg2list <- function(p){ } } - ## axes titles + # axes titles annotations[[nann]] <- make.label(xaxis.title, 0.5, -outer.margin, @@ -512,17 +512,17 @@ gg2list <- function(p){ layout$annotations <- annotations } - ## Remove legend if theme has no legend position + # Remove legend if theme has no legend position layout$showlegend <- !(theme.pars$legend.position=="none") - ## Main plot title. + # Main plot title. layout$title <- built$plot$labels$title - ## Background color. + # Background color. layout$plot_bgcolor <- toRGB(theme.pars$panel.background$fill) layout$paper_bgcolor <- toRGB(theme.pars$plot.background$fill) - ## Legend. + # Legend. layout$margin$r <- 10 if (exists("increase_margin_r")) { layout$margin$r <- 60 @@ -563,23 +563,23 @@ gg2list <- function(p){ layout$annotations <- annotations } - ## Family font for text + # Family font for text if (!is.null(theme.pars$text$family)) { layout$titlefont$family <- theme.pars$text$family layout$legend$font$family <- theme.pars$text$family } - ## Family font for title + # Family font for title if (!is.null(theme.pars$plot.title$family)) { layout$titlefont$family <- theme.pars$plot.title$family } - ## Family font for legend + # Family font for legend if (!is.null(theme.pars$legend.text$family)) { layout$legend$font$family <- theme.pars$legend.text$family } - ## Bold, italic and bold.italic face for text + # Bold, italic and bold.italic face for text text_face <- theme.pars$text$face if (!is.null(text_face)) { if (text_face=="bold") { @@ -597,7 +597,7 @@ gg2list <- function(p){ } } - ## Bold, italic and bold.italic face for title + # Bold, italic and bold.italic face for title title_face <- theme.pars$plot.title$face if (!is.null(title_face)) { if (title_face=="bold") { @@ -609,7 +609,7 @@ gg2list <- function(p){ } } - ## Bold, italic, and bold.italic face for axis title + # Bold, italic, and bold.italic face for axis title title_face <- list(theme.pars$axis.title.y$face, theme.pars$axis.title.x$face) sub_elem <- c("yaxis", "xaxis") From 1d4c09ae8ebef5a1365112a032e7a60b1178e3db Mon Sep 17 00:00:00 2001 From: "Anonymous@example.com" <cpsievert1@gmail.com> Date: Fri, 20 Feb 2015 14:22:48 -0600 Subject: [PATCH 6/9] rename facet test and save results --- tests/testthat/{test-facets.R => test-ggplot-facets.R} | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) rename tests/testthat/{test-facets.R => test-ggplot-facets.R} (91%) diff --git a/tests/testthat/test-facets.R b/tests/testthat/test-ggplot-facets.R similarity index 91% rename from tests/testthat/test-facets.R rename to tests/testthat/test-ggplot-facets.R index a52348bff9..e39575f853 100644 --- a/tests/testthat/test-facets.R +++ b/tests/testthat/test-ggplot-facets.R @@ -51,7 +51,6 @@ test_that("3 facets becomes 3 panels", { # expect a certain number of _unique_ [x/y] axes expect_axes <- function(info, n, axis = "x") { pattern <- paste0("^", axis, "axis([0-9]+)?$") - #n.axes <- length(grep(pattern, names(info$kwargs$layout))) axes <- with(info$kwargs, layout[grepl(pattern, names(layout))]) n.axes <- length(axes) ranges <- do.call("rbind", lapply(axes, function(x) x$range)) @@ -62,16 +61,19 @@ no_panels <- ggplot(mtcars, aes(mpg, wt)) + geom_point() test_that("facet_wrap(..., scales = 'free') creates interior scales", { free_both <- no_panels + facet_wrap(~am+vs, scales = "free") + save_outputs(free_both, "facet_wrap_free") info <- gg2list(free_both) expect_axes(info, 4L) expect_axes(info, 4L, "y") free_y <- no_panels + facet_wrap(~am+vs, scales = "free_y") + save_outputs(free_y, "facet_wrap_free_y") info <- gg2list(free_y) expect_axes(info, 1L) expect_axes(info, 4L, "y") free_x <- no_panels + facet_wrap(~am+vs, scales = "free_x") + save_outputs(free_x, "facet_wrap_free_x") info <- gg2list(free_x) expect_axes(info, 4L) expect_axes(info, 1L, "y") @@ -79,17 +81,20 @@ test_that("facet_wrap(..., scales = 'free') creates interior scales", { test_that("facet_grid(..., scales = 'free') doesnt create interior scales.", { free_both <- no_panels + facet_grid(vs~am, scales = "free") + save_outputs(free_both, "facet_grid_free") info <- gg2list(free_both) expect_axes(info, 2L) expect_axes(info, 2L, "y") free_y <- no_panels + facet_grid(vs~am, scales = "free_y") + save_outputs(free_y, "facet_grid_free_y") info <- gg2list(free_y) expect_axes(info, 1L) expect_axes(info, 2L, "y") free_x <- no_panels + facet_grid(vs~am, scales = "free_x") + save_outputs(free_x, "facet_grid_free_x") info <- gg2list(free_x) expect_axes(info, 2L) expect_axes(info, 1L, "y") -}) \ No newline at end of file +}) From 05d29e763e3984501535998de9a1aef21d86902f Mon Sep 17 00:00:00 2001 From: "Anonymous@example.com" <cpsievert1@gmail.com> Date: Thu, 26 Feb 2015 21:06:21 -0500 Subject: [PATCH 7/9] fix attempt --- R/ggplotly.R | 148 ++++++++++++++++++++++++--------------------------- 1 file changed, 71 insertions(+), 77 deletions(-) diff --git a/R/ggplotly.R b/R/ggplotly.R index 0a86305d03..b041315746 100644 --- a/R/ggplotly.R +++ b/R/ggplotly.R @@ -396,17 +396,14 @@ gg2list <- function(p){ y <- row * row.size ymin <- y - row.size ymax <- y - inner.margin - xaxis.name <- if (panel == 1) "xaxis" else paste0("xaxis", panel) - yaxis.name <- if (panel == 1) "yaxis" else paste0("yaxis", panel) - #layout defaults that won't change depending on the type of facet - layout[[xaxis.name]] <- orig.xaxis - layout[[xaxis.name]]$domain <- c(xmin, xmax) - layout[[xaxis.name]]$title <- NULL - layout[[yaxis.name]] <- orig.yaxis - layout[[yaxis.name]]$domain <- c(ymin, ymax) - layout[[yaxis.name]]$title <- NULL - # facet_wrap allows for a different x/y axis on each panel + # assume grid layout by default where axes are restrict to the exterior + xaxis.name <- if (col == 1) "xaxis" else paste0("xaxis", col) + yaxis.name <- if (row == 1) "yaxis" else paste0("yaxis", row) + # anchor needs to be incremented if the corresponding axis is "free" + xanchor <- "y" + yanchor <- "x" if ("wrap" %in% class(p$facet)) { + # in wrap layout, axes can be drawn on interior (if scales are free) # make room for facet strip label ymax <- ymax - 0.04 # make room for yaxis labels (this should be a function of label size) @@ -422,19 +419,26 @@ gg2list <- function(p){ ymin <- ymin + 0.02 } if (p$facet$free$y && panel > 1) { - # is it safe to assume npanels == ntraces? + # draw a y-axis on each panel yaxis.name <- paste0("yaxis", panel) trace.list[[i]]$yaxis <- paste0("y", panel) - layout[[yaxis.name]]$anchor <- paste0("x", panel) - } + yanchor <- if (p$facet$free$x) paste0("x", panel) else paste0("x",col) + } if (p$facet$free$x && panel > 1) { + # draw an x-axis on each panel xaxis.name <- paste0("xaxis", panel) trace.list[[i]]$xaxis <- paste0("x", panel) - layout[[xaxis.name]]$anchor <- paste0("y", panel) + xanchor <- if (p$facet$free$y) paste0("y", panel) else paste0("y",row) } - layout[[xaxis.name]]$domain <- c(xmin, xmax) - layout[[yaxis.name]]$domain <- c(ymin, ymax) - } + } + layout[[xaxis.name]] <- orig.xaxis + layout[[xaxis.name]]$domain <- c(xmin, xmax) + layout[[xaxis.name]]$anchor <- xanchor + layout[[xaxis.name]]$title <- NULL + layout[[yaxis.name]] <- orig.yaxis + layout[[yaxis.name]]$domain <- c(ymin, ymax) + layout[[yaxis.name]]$anchor <- yanchor + layout[[yaxis.name]]$title <- NULL if (is.null(layout[[xaxis.name]]$anchor)) layout[[xaxis.name]]$anchor <- "y" if (is.null(layout[[yaxis.name]]$anchor)) @@ -456,70 +460,60 @@ gg2list <- function(p){ list(text=text, showarrow=FALSE, x=x, y=y, ax=0, ay=0, xref="paper", yref="paper", xanchor=xanchor, yanchor=yanchor, textangle=textangle) - if ("grid" %in% class(p$facet)) { frows <- names(p$facet$rows) nann <- 1 - make.label <- function(text, x, y, xanchor="auto", yanchor="auto", textangle=0) - list(text=text, showarrow=FALSE, x=x, y=y, ax=0, ay=0, - xref="paper", yref="paper", xanchor=xanchor, yanchor=yanchor, - textangle=textangle) - if ("grid" %in% class(p$facet)) { - frows <- names(p$facet$rows) - nann <- 1 - - for (i in seq_len(max(gglayout$ROW))) { - text <- paste(lapply(gglayout[gglayout$ROW == i, frows, drop=FALSE][1,], - as.character), - collapse=", ") - if (text != "") { # to not create extra annotations - increase_margin_r <- TRUE - annotations[[nann]] <- make.label(text, - 1 + outer.margin - 0.04, - row.size * (max(gglayout$ROW)-i+0.5), - xanchor="center", - textangle=90) - nann <- nann + 1 - } - } - fcols <- names(p$facet$cols) - for (i in seq_len(max(gglayout$COL))) { - text <- paste(lapply(gglayout[gglayout$COL == i, fcols, drop=FALSE][1,], - as.character), - collapse=", ") - if (text!="") { - annotations[[nann]] <- make.label(text, - col.size * (i-0.5) - inner.margin/2, - 1 + outer.margin, - xanchor="center") - nann <- nann + 1 - } - } - - # add empty traces everywhere so that the background shows even if there - # is no data for a facet - for (r in seq_len(max(gglayout$ROW))) - for (c in seq_len(max(gglayout$COL))) - trace.list <- c(trace.list, list(list(xaxis=paste0("x", c), yaxis=paste0("y", r), showlegend=FALSE))) - } else if ("wrap" %in% class(p$facet)) { - facets <- names(p$facet$facets) - for (i in seq_len(max(as.numeric(gglayout$PANEL)))) { - ix <- gglayout$PANEL == i - row <- gglayout$ROW[ix] - col <- gglayout$COL[ix] - text <- paste(lapply(gglayout[ix, facets, drop=FALSE][1,], - as.character), - collapse=", ") - annotations[[nann]] <- make.label(text, - col.size * (col-0.5) - inner.margin/2, - row.size * (max(gglayout$ROW) - row + 0.985), + for (i in seq_len(max(gglayout$ROW))) { + text <- paste(lapply(gglayout[gglayout$ROW == i, frows, drop=FALSE][1,], + as.character), + collapse=", ") + if (text != "") { # to not create extra annotations + increase_margin_r <- TRUE + annotations[[nann]] <- make.label(text, + 1 + outer.margin - 0.04, + row.size * (max(gglayout$ROW)-i+0.5), xanchor="center", - yanchor="top") + textangle=90) + nann <- nann + 1 + } + } + fcols <- names(p$facet$cols) + for (i in seq_len(max(gglayout$COL))) { + text <- paste(lapply(gglayout[gglayout$COL == i, fcols, drop=FALSE][1,], + as.character), + collapse=", ") + if (text!="") { + annotations[[nann]] <- make.label(text, + col.size * (i-0.5) - inner.margin/2, + 1 + outer.margin, + xanchor="center") nann <- nann + 1 } } + # add empty traces everywhere so that the background shows even if there + # is no data for a facet + for (r in seq_len(max(gglayout$ROW))) + for (c in seq_len(max(gglayout$COL))) + trace.list <- c(trace.list, list(list(xaxis=paste0("x", c), yaxis=paste0("y", r), showlegend=FALSE))) + } else if ("wrap" %in% class(p$facet)) { + facets <- names(p$facet$facets) + for (i in seq_len(max(as.numeric(gglayout$PANEL)))) { + ix <- gglayout$PANEL == i + row <- gglayout$ROW[ix] + col <- gglayout$COL[ix] + text <- paste(lapply(gglayout[ix, facets, drop=FALSE][1,], + as.character), + collapse=", ") + annotations[[nann]] <- make.label(text, + col.size * (col-0.5) - inner.margin/2, + row.size * (max(gglayout$ROW) - row + 0.985), + xanchor="center", + yanchor="top") + nann <- nann + 1 + } + # axes titles annotations[[nann]] <- make.label(xaxis.title, 0.5, @@ -553,7 +547,6 @@ gg2list <- function(p){ layout$legend <- list(bordercolor="transparent", x=1.05, y=1/2, xanchor="center", yanchor="top") - # Workaround for removing unnecessary legends. # [markUnique != "x"] is for boxplot's particular case. if (any(names(layer.aes) %in% markUnique[markUnique != "x"]) == FALSE) @@ -687,10 +680,11 @@ gg2list <- function(p){ for(other.i in seq_along(not.merged)){ other <- not.merged[[other.i]] criteria <- c() - for(must.be.equal in c("x", "y", "xaxis", "yaxis")){ - other.attr <- other[[must.be.equal]] - tr.attr <- tr[[must.be.equal]] - criteria[[must.be.equal]] <- isTRUE(all.equal(other.attr, tr.attr)) + must.be.equal <- c("x", "y", "xaxis", "yaxis") + for(j in must.be.equal){ + other.attr <- other[[j]] + tr.attr <- tr[[j]] + criteria[[j]] <- isTRUE(all.equal(other.attr, tr.attr)) } if(all(criteria)){ can.merge[[other.i]] <- TRUE From f800c1249fc36072c1b4dd775b88659144b80f17 Mon Sep 17 00:00:00 2001 From: cpsievert <cpsievert1@gmail.com> Date: Mon, 9 Mar 2015 16:58:16 -0500 Subject: [PATCH 8/9] Fix annotations for facet_grid() --- R/ggplotly.R | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/R/ggplotly.R b/R/ggplotly.R index c0231e2e20..bab347df2d 100644 --- a/R/ggplotly.R +++ b/R/ggplotly.R @@ -527,7 +527,7 @@ gg2list <- function(p){ yanchor="top") nann <- nann + 1 } - + } # axes titles annotations[[nann]] <- make.label(xaxis.title, 0.5, @@ -538,9 +538,7 @@ gg2list <- function(p){ -outer.margin, 0.5, textangle=-90) - - layout$annotations <- annotations - } + layout$annotations <- annotations } # Remove legend if theme has no legend position From f9e252af61276fd40168fc8916024877c7f2f6a3 Mon Sep 17 00:00:00 2001 From: cpsievert <cpsievert1@gmail.com> Date: Tue, 10 Mar 2015 11:58:20 -0500 Subject: [PATCH 9/9] Bump version, update NEWS, add Carson as author --- DESCRIPTION | 6 ++++-- NEWS | 4 ++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index cfdb548b5c..2bffaf5d01 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,7 +1,7 @@ Package: plotly Type: Package Title: Interactive, publication-quality graphs online. -Version: 0.5.22 +Version: 0.5.23 Authors@R: c(person("Chris", "Parmer", role = c("aut", "cre"), email = "chris@plot.ly"), person("Scott", "Chamberlain", role = "aut", @@ -13,7 +13,9 @@ Authors@R: c(person("Chris", "Parmer", role = c("aut", "cre"), person("Marianne", "Corvellec", role="aut", email="marianne@plot.ly"), person("Pedro", "Despouy", role="aut", - email="pedro@plot.ly")) + email="pedro@plot.ly"), + person("Carson", "Sievert", role="aut", + email="cpsievert1@gmail.com")) Author: Chris Parmer Maintainer: Marianne Corvellec <marianne@plot.ly> License: MIT + file LICENSE diff --git a/NEWS b/NEWS index 21360e5661..9f48910e6d 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,7 @@ +0.5.23 -- 10 March 2015. + +Implemented #167 + 0.5.22 -- 2 March 2015. Fixes for ylim() #171.