Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

plt_add() #246

Merged
merged 5 commits into from
Nov 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ S3method(tinyplot,formula)
export(draw_legend)
export(get_saved_par)
export(plt)
export(plt_add)
export(tinyplot)
export(tinyplot_add)
export(tpar)
export(type_area)
export(type_boxplot)
Expand Down
8 changes: 8 additions & 0 deletions R/tinyplot.R
Original file line number Diff line number Diff line change
Expand Up @@ -536,6 +536,14 @@ tinyplot.default = function(
...
) {

# save for plt_add()
options(tinyplot_last_call = match.call(tinyplot,
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be consistent with the rest of the package infrastructure, let's rather save this to the existing .tinyplot_env. This is what we use in R/get_saved_par.R, for an example:

tinyplot/R/get_saved_par.R

Lines 104 to 116 in 02d7806

#' @export
get_saved_par = function(when = c("before", "after")) {
when = match.arg(when)
par_env_name = paste0(".saved_par_", when)
return(get(par_env_name, envir = get(".tinyplot_env", envir = parent.env(environment()))))
}
# (non-exported) companion function(s) for setting the original pars
set_saved_par = function(when = c("before", "after"), value) {
when = match.arg(when)
par_env_name = paste0(".saved_par_", when)
assign(par_env_name, value, envir = get(".tinyplot_env", envir = parent.env(environment())))
}

We'll need to assign NULL values to this call object at startup in R/zzz.R too, e.g.

assign(".saved_par_after", NULL, envir = get(".tinyplot_env", envir = parent.env(environment())))

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately, I was unable to get this to work. I kept getting an error in the tinyplot.density tests. The message is obscure, having to do with a do.call and a substitute. I just can figure this out.

My guess is that this will disappear by itself when we refactor the density code.

In the meantime, I left TODOs in both files with the appropriate save code that we can eventually.

Feel free to take a shot at it if you want, but I have to give up for now.

call = sys.call(sys.parent()), expand.dots = TRUE))

## TODO: remove the global option above and move to this when density is refactored
# cal = match.call(call = sys.call(sys.parent()), expand.dots = TRUE)
# assign(".last_call", cal, envir = get(".tinyplot_env", envir = parent.env(environment())))

dots = list(...)

if (isTRUE(add)) legend = FALSE
Expand Down
52 changes: 52 additions & 0 deletions R/tinyplot_add.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
#' Add new elements to the current `tinyplot`
#'
#' @description
#' This function grabs the last `tinyplot` call, sets `add=TRUE`, and
#' overwrites some of the arguments with those explicitly supplied by the
#' user. Then, the call is evaluated again to add a layer to an existing plot.
#'
#' @details
#' Note: the automatic legend for the added elements will be turned off.
#'
#' @param ... All named arguments override arguments from the previous calls.
#' Arguments not supplied to `tinyplot_add()` remain unchanged from the previous call.
#'
#' @examples
#' library(tinyplot)
#'
#' tinyplot(Sepal.Width ~ Sepal.Length | Species,
#' facet = ~Species,
#' data = iris,
#' type = type_lm())
#'
#' tinyplot_add(type = "p")
#'
#' @returns No return value, called for side effect of producing a plot.
#'
#' @export
tinyplot_add <- function(...) {
cal = getOption("tinyplot_last_call", default = NULL)

## TODO: remove the global option above and move to this when density is refactored
# cal = get(".last_call", envir = get(".tinyplot_env", envir = parent.env(environment())))

if (is.null(cal)) {
stop("No previous tinyplot call found.")
}

args = list(...)
for (n in names(args)) {
if (n != "") {
cal[[n]] = args[[n]]
}
}
cal[["add"]] = TRUE
eval(cal)
}



#' @export
#' @name plt_add
#' @rdname tinyplot_add
plt_add = tinyplot_add
1 change: 1 addition & 0 deletions R/zzz.R
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@

assign(".saved_par_before", NULL, envir = get(".tinyplot_env", envir = parent.env(environment())))
assign(".saved_par_after", NULL, envir = get(".tinyplot_env", envir = parent.env(environment())))
assign(".last_call", NULL, envir = get(".tinyplot_env", envir = parent.env(environment())))

globalVariables(c(
"add",
Expand Down
6 changes: 4 additions & 2 deletions altdoc/quarto_website.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,12 @@ website:
contents:
- section: Plotting functions
contents:
- text: draw_legend
file: man/draw_legend.qmd
- text: tinyplot
file: man/tinyplot.qmd
- text: tinyplot_add
file: man/tinyplot_add.qmd
- text: draw_legend
file: man/draw_legend.qmd
- section: Plot types
contents:
#
Expand Down
362 changes: 362 additions & 0 deletions inst/tinytest/_tinysnapshot/tinyplot_add.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 14 additions & 0 deletions inst/tinytest/test-tinyplot_add.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
source("helpers.R")
using("tinysnapshot")

## error message cannot be tested in this suite
# expect_error(tinyplot_add(type = "p"), pattern = "No previous tinyplot")

f = function() {
tinyplot(Sepal.Width ~ Sepal.Length | Species,
facet = ~Species,
data = iris,
type = "p")
tinyplot_add(type = type_lm())
}
expect_snapshot_plot(f, label = "tinyplot_add")
37 changes: 37 additions & 0 deletions man/tinyplot_add.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

28 changes: 22 additions & 6 deletions vignettes/introduction.qmd
Original file line number Diff line number Diff line change
Expand Up @@ -409,12 +409,7 @@ tinyplot(
ylim = c(50, 100),
main = "Actual and predicted air temperatures"
)
tinyplot(
Temp ~ Day | Month, aq,
facet = "by", facet.args = list(bg = "grey90"),
palette = "dark2",
add = TRUE
)
tinyplot_add(type = "p")
```

Note that the `facet` argument also accepts a _two-sided_ formula for arranging
Expand All @@ -438,6 +433,27 @@ tinyplot(
The `facet.args` customizations can also be set globally via the `tpar()`
function, which provides a nice segue to our penultimate section.

## Layers

In many contexts, it is convenient to build plots step-by-step, adding layers with different elements on top of a base.

The `tinyplot()` function includes an `add=TRUE` argument that adds element to an existing plot, instead of drawing a new window. This argument is useful, but a bit verbose, as it requires users to make very similar successive calls, with many shared arguments.

For convenience, the package also includes a `tinyplot_add()` function (alias `plt_add()`), which captures the last `tinyplot()` call, keeps all the same arguments, and modifies just the arguments that the user explicitly wants to change.

In the following example, we first draw linear regression lines with facets and group coloring. Then, we add the original data points using `tinyplot_add()`. Notice that the same grouping and data options are carried over to points, without having to specify them in the `tinyplot_add()` function.

```{r}
library(tinyplot)

tinyplot(Sepal.Width ~ Sepal.Length | Species,
facet = ~Species,
data = iris,
type = "p")

tinyplot_add(type = type_lm())
```

## Themes

Customizing your plots further is straightforward, whether that is done directly
Expand Down
Loading