From 30c93bcb4da2214bcc2cd25738f7b53395fd99be Mon Sep 17 00:00:00 2001 From: Grant McDermott Date: Thu, 7 May 2026 17:17:12 -0700 Subject: [PATCH 01/13] grid characters --- DESCRIPTION | 2 +- R/assertions.R | 9 ++++++ R/facet.R | 71 ++++++++++++++++++++++++++++++++--------- R/tinyplot.R | 9 ++++-- R/tpar.R | 4 +-- man/tinyplot-package.Rd | 1 + man/tinyplot.Rd | 9 ++++-- man/tpar.Rd | 2 +- 8 files changed, 84 insertions(+), 23 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 4f4886b2..89819c42 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -54,7 +54,7 @@ Suggests: knitr Config/Needs/website: altdoc (>= 0.7.2), future.apply Encoding: UTF-8 -RoxygenNote: 7.3.3 URL: https://grantmcdermott.com/tinyplot/ BugReports: https://github.com/grantmcdermott/tinyplot/issues Roxygen: list(markdown = TRUE) +Config/roxygen2/version: 8.0.0 diff --git a/R/assertions.R b/R/assertions.R index 5e48f97d..1b411daa 100644 --- a/R/assertions.R +++ b/R/assertions.R @@ -90,6 +90,15 @@ assert_length = function(x, len = 1, null.ok = FALSE, name = as.character(substi } } +assert_grid = function(x, null.ok = FALSE, name = as.character(substitute(x))) { + if (is.null(x) && isTRUE(null.ok)) return(invisible(TRUE)) + if (is.logical(x) && length(x) == 1) return(invisible(TRUE)) + valid_strings = c("x", "y", "xy", "X", "Y", "XY") + if (is.character(x) && length(x) == 1 && x %in% valid_strings) return(invisible(TRUE)) + msg = sprintf("`%s` must be a logical flag or one of: %s", name, paste(valid_strings, collapse = ", ")) + stop(msg, call. = FALSE) +} + assert_logical = function(x, null.ok = FALSE, name = as.character(substitute(x))) { if (is.null(x) && isTRUE(null.ok)) { return(invisible(TRUE)) diff --git a/R/facet.R b/R/facet.R index 3d1b1b95..237887d0 100644 --- a/R/facet.R +++ b/R/facet.R @@ -514,34 +514,75 @@ draw_facet_window = function( # panel grid lines if (is.null(grid)) grid = get_tpar("grid", tpar_list = tpars) - if (!is.null(grid)) { - if (is.logical(grid)) { - ## If grid is TRUE create a default grid. Rather than just calling the default grid() - ## abline(... = pretty(extendrange(...)), ...) is used. Reason: pretty() is generic - ## and works better for axes based on date/time classes. Exception: For axes in logs, - ## resort to using grid() which is likely better handled there. - if (isTRUE(grid)) { - gnx = gny = NULL + if (!is.null(grid) && !isFALSE(grid)) { + gcol = get_tpar("grid.col", tpar_list = tpars) + glty = get_tpar("grid.lty", tpar_list = tpars) + glwd = get_tpar("grid.lwd", tpar_list = tpars) + + if (isTRUE(grid)) { + draw_x = draw_y = TRUE + fine_x = fine_y = FALSE + } else if (is.character(grid)) { + draw_x = grepl("x", grid, fixed = TRUE) || grepl("X", grid, fixed = TRUE) + draw_y = grepl("y", grid, fixed = TRUE) || grepl("Y", grid, fixed = TRUE) + fine_x = grepl("x", grid, fixed = TRUE) + fine_y = grepl("y", grid, fixed = TRUE) + } else { + grid + draw_x = draw_y = FALSE + } + + if (draw_x || draw_y) { + gnx = gny = NULL + + if (draw_x) { if (!is.null(xaxb)) { - abline(v = xaxb, col = get_tpar("grid.col", tpar_list = tpars), lty = get_tpar("grid.lty", tpar_list = tpars), lwd = get_tpar("grid.lwd", tpar_list = tpars)) + xg = xaxb + if (fine_x && length(xg) >= 2L) { + xg2 = (xg[2L] - xg[1L]) / 2 + xg = seq(xg[1L] - xg2, xg[length(xg)] + xg2, by = xg2) + xg = xg[xg >= xlim[1L] & xg <= xlim[2L]] + } + abline(v = xg, col = gcol, lty = glty, lwd = glwd) gnx = NA } else if (!any(c(par("xlog"), type == "boxplot"))) { xg = if (!inherits(x, c("POSIXt", "Date"))) axTicks(side = 1) else axTicksDateTime(side = 1, x = x) - abline(v = xg, col = get_tpar("grid.col", tpar_list = tpars), lty = get_tpar("grid.lty", tpar_list = tpars), lwd = get_tpar("grid.lwd", tpar_list = tpars)) + if (fine_x && length(xg) >= 2L) { + xg2 = (xg[2L] - xg[1L]) / 2 + xg = seq(xg[1L] - xg2, xg[length(xg)] + xg2, by = xg2) + xg = xg[xg >= xlim[1L] & xg <= xlim[2L]] + } + abline(v = xg, col = gcol, lty = glty, lwd = glwd) gnx = NA } + } + + if (draw_y) { if (!is.null(yaxb)) { - abline(h = yaxb, col = get_tpar("grid.col", tpar_list = tpars), lty = get_tpar("grid.lty", tpar_list = tpars), lwd = get_tpar("grid.lwd", tpar_list = tpars)) + yg = yaxb + if (fine_y && length(yg) >= 2L) { + yg2 = (yg[2L] - yg[1L]) / 2 + yg = seq(yg[1L] - yg2, yg[length(yg)] + yg2, by = yg2) + yg = yg[yg >= ylim[1L] & yg <= ylim[2L]] + } + abline(h = yg, col = gcol, lty = glty, lwd = glwd) gny = NA } else if (!any(c(par("ylog"), type == "boxplot"))) { yg = if (!inherits(y, c("POSIXt", "Date"))) axTicks(side = 2) else axTicksDateTime(side = 2, x = x) - abline(h = yg, col = get_tpar("grid.col", tpar_list = tpars), lty = get_tpar("grid.lty", tpar_list = tpars), lwd = get_tpar("grid.lwd", tpar_list = tpars)) + if (fine_y && length(yg) >= 2L) { + yg2 = (yg[2L] - yg[1L]) / 2 + yg = seq(yg[1L] - yg2, yg[length(yg)] + yg2, by = yg2) + yg = yg[yg >= ylim[1L] & yg <= ylim[2L]] + } + abline(h = yg, col = gcol, lty = glty, lwd = glwd) gny = NA } - grid(nx = gnx, ny = gny, col = get_tpar("grid.col", tpar_list = tpars), lty = get_tpar("grid.lty", tpar_list = tpars), lwd = get_tpar("grid.lwd", tpar_list = tpars)) } - } else { - grid + + # Handle log-scale axes via grid() for any axes not yet drawn + grid(nx = gnx %||% if (draw_x) NULL else NA, + ny = gny %||% if (draw_y) NULL else NA, + col = gcol, lty = glty, lwd = glwd) } } diff --git a/R/tinyplot.R b/R/tinyplot.R index ef289e86..dedced43 100644 --- a/R/tinyplot.R +++ b/R/tinyplot.R @@ -197,8 +197,13 @@ #' the plot. Can also use `frame` as an acceptable argument alias. #' The default is to draw a frame if both axis types (set via `axes`, `xaxt`, #' or `yaxt`) include axis lines. -#' @param grid argument for plotting a background panel grid, one of either: -#' - a logical (i.e., `TRUE` to draw the grid), or +#' @param grid argument for plotting a background panel grid, one of: +#' - a logical (i.e., `TRUE` to draw the grid on both axes), +#' - a character string controlling which axes get grid lines and at what +#' resolution. Uppercase letters (`"X"`, `"Y"`, `"XY"`) draw grid lines at +#' the standard axis tick positions (with the latter equivalent to `TRUE`), +#' while lowercase letters (`"x"`, `"y"`, `"xy"`) draw a finer grid with +#' additional lines at the midpoints between ticks, or #' - a panel grid plotting function like `grid()`. #' Note that this argument replaces the `panel.first` and `panel.last` #' arguments from base `plot()` and tries to make the process more seamless diff --git a/R/tpar.R b/R/tpar.R index 763b6374..bedb76df 100644 --- a/R/tpar.R +++ b/R/tpar.R @@ -65,7 +65,7 @@ #' * `grid.col`: Character or (integer) numeric that specifies the color of the panel grid lines. Defaults to `"lightgray"`. #' * `grid.lty`: Character or (integer) numeric that specifies the line type of the panel grid lines. Defaults to `"dotted"`. #' * `grid.lwd`: Non-negative numeric giving the line width of the panel grid lines. Defaults to `1`. -#' * `grid`: Logical indicating whether a background panel grid should be added to plots automatically. Defaults to `NULL`, which is equivalent to `FALSE`. +#' * `grid`: Logical or character indicating whether a background panel grid should be added to plots automatically. Defaults to `NULL`, which is equivalent to `FALSE`. In addition to logical values, a character string can be used to control axis-specific grids: uppercase letters (`"X"`, `"Y"`, `"XY"`) draw grid lines at the standard axis tick positions (equivalent to `TRUE`), while lowercase letters (`"x"`, `"y"`, `"xy"`) draw a finer grid with additional lines at the midpoints between ticks. #' * `lmar`: A numeric vector of form `c(inner, outer)` that gives the margin padding, in terms of lines, around the automatic `tinyplot` legend. Defaults to `c(1.0, 0.1)`. The inner margin is the gap between the legend and the plot region, and the outer margin is the gap between the legend and the edge of the graphics device. #' * `palette.qualitative`: Palette for qualitative colors. See the `palette` argument in `?tinyplot`. #' * `palette.sequential`: Palette for sequential colors. See the `palette` argument in `?tinyplot`. @@ -276,7 +276,7 @@ assert_tpar = function(.tpar) { assert_numeric(.tpar[["lmar"]], len = 2, null.ok = TRUE, name = "lmar") assert_numeric(.tpar[["ribbon.alpha"]], len = 1, lower = 0, upper = 1, null.ok = TRUE, name = "ribbon.alpha") assert_numeric(.tpar[["grid.lwd"]], len = 1, lower = 0, null.ok = TRUE, name = "grid.lwd") - assert_flag(.tpar[["grid"]], null.ok = TRUE, name = "grid") + assert_grid(.tpar[["grid"]], null.ok = TRUE, name = "grid") assert_numeric(.tpar[["file.res"]], len = 1, lower = 0, null.ok = TRUE, name = "file.res") assert_numeric(.tpar[["file.height"]], len = 1, lower = 0, null.ok = TRUE, name = "file.height") assert_numeric(.tpar[["file.width"]], len = 1, lower = 0, null.ok = TRUE, name = "file.width") diff --git a/man/tinyplot-package.Rd b/man/tinyplot-package.Rd index 13b5d091..5684b661 100644 --- a/man/tinyplot-package.Rd +++ b/man/tinyplot-package.Rd @@ -20,6 +20,7 @@ Useful links: Authors: \itemize{ + \item Grant McDermott \email{contact@grantmcdermott.com} (\href{https://orcid.org/0000-0001-7883-8573}{ORCID}) \item Vincent Arel-Bundock \email{vincent.arel-bundock@umontreal.ca} (\href{https://orcid.org/0000-0003-1995-6531}{ORCID}) \item Achim Zeileis \email{Achim.Zeileis@R-project.org} (\href{https://orcid.org/0000-0003-0918-3766}{ORCID}) } diff --git a/man/tinyplot.Rd b/man/tinyplot.Rd index 503f1579..ccc19b8a 100644 --- a/man/tinyplot.Rd +++ b/man/tinyplot.Rd @@ -331,9 +331,14 @@ the plot. Can also use \code{frame} as an acceptable argument alias. The default is to draw a frame if both axis types (set via \code{axes}, \code{xaxt}, or \code{yaxt}) include axis lines.} -\item{grid}{argument for plotting a background panel grid, one of either: +\item{grid}{argument for plotting a background panel grid, one of: \itemize{ -\item a logical (i.e., \code{TRUE} to draw the grid), or +\item a logical (i.e., \code{TRUE} to draw the grid on both axes), +\item a character string controlling which axes get grid lines and at what +resolution. Uppercase letters (\code{"X"}, \code{"Y"}, \code{"XY"}) draw grid lines at +the standard axis tick positions (with the latter equivalent to \code{TRUE}), +while lowercase letters (\code{"x"}, \code{"y"}, \code{"xy"}) draw a finer grid with +additional lines at the midpoints between ticks, or \item a panel grid plotting function like \code{grid()}. Note that this argument replaces the \code{panel.first} and \code{panel.last} arguments from base \code{plot()} and tries to make the process more seamless diff --git a/man/tpar.Rd b/man/tpar.Rd index c1fc5914..eea89829 100644 --- a/man/tpar.Rd +++ b/man/tpar.Rd @@ -76,7 +76,7 @@ you should rather use \code{par()} instead. \item \code{grid.col}: Character or (integer) numeric that specifies the color of the panel grid lines. Defaults to \code{"lightgray"}. \item \code{grid.lty}: Character or (integer) numeric that specifies the line type of the panel grid lines. Defaults to \code{"dotted"}. \item \code{grid.lwd}: Non-negative numeric giving the line width of the panel grid lines. Defaults to \code{1}. -\item \code{grid}: Logical indicating whether a background panel grid should be added to plots automatically. Defaults to \code{NULL}, which is equivalent to \code{FALSE}. +\item \code{grid}: Logical or character indicating whether a background panel grid should be added to plots automatically. Defaults to \code{NULL}, which is equivalent to \code{FALSE}. In addition to logical values, a character string can be used to control axis-specific grids: uppercase letters (\code{"X"}, \code{"Y"}, \code{"XY"}) draw grid lines at the standard axis tick positions (equivalent to \code{TRUE}), while lowercase letters (\code{"x"}, \code{"y"}, \code{"xy"}) draw a finer grid with additional lines at the midpoints between ticks. \item \code{lmar}: A numeric vector of form \code{c(inner, outer)} that gives the margin padding, in terms of lines, around the automatic \code{tinyplot} legend. Defaults to \code{c(1.0, 0.1)}. The inner margin is the gap between the legend and the plot region, and the outer margin is the gap between the legend and the edge of the graphics device. \item \code{palette.qualitative}: Palette for qualitative colors. See the \code{palette} argument in \code{?tinyplot}. \item \code{palette.sequential}: Palette for sequential colors. See the \code{palette} argument in \code{?tinyplot}. From b1829c20cf82abd5b40517ac1e6e8b86e00f8f71 Mon Sep 17 00:00:00 2001 From: Grant McDermott Date: Thu, 7 May 2026 17:30:01 -0700 Subject: [PATCH 02/13] safer logic --- R/facet.R | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/R/facet.R b/R/facet.R index 237887d0..71596648 100644 --- a/R/facet.R +++ b/R/facet.R @@ -540,8 +540,7 @@ draw_facet_window = function( xg = xaxb if (fine_x && length(xg) >= 2L) { xg2 = (xg[2L] - xg[1L]) / 2 - xg = seq(xg[1L] - xg2, xg[length(xg)] + xg2, by = xg2) - xg = xg[xg >= xlim[1L] & xg <= xlim[2L]] + xg = seq(floor(xlim[1L] / xg2) * xg2, ceiling(xlim[2L] / xg2) * xg2, by = xg2) } abline(v = xg, col = gcol, lty = glty, lwd = glwd) gnx = NA @@ -549,8 +548,7 @@ draw_facet_window = function( xg = if (!inherits(x, c("POSIXt", "Date"))) axTicks(side = 1) else axTicksDateTime(side = 1, x = x) if (fine_x && length(xg) >= 2L) { xg2 = (xg[2L] - xg[1L]) / 2 - xg = seq(xg[1L] - xg2, xg[length(xg)] + xg2, by = xg2) - xg = xg[xg >= xlim[1L] & xg <= xlim[2L]] + xg = seq(floor(xlim[1L] / xg2) * xg2, ceiling(xlim[2L] / xg2) * xg2, by = xg2) } abline(v = xg, col = gcol, lty = glty, lwd = glwd) gnx = NA @@ -562,8 +560,7 @@ draw_facet_window = function( yg = yaxb if (fine_y && length(yg) >= 2L) { yg2 = (yg[2L] - yg[1L]) / 2 - yg = seq(yg[1L] - yg2, yg[length(yg)] + yg2, by = yg2) - yg = yg[yg >= ylim[1L] & yg <= ylim[2L]] + yg = seq(floor(ylim[1L] / yg2) * yg2, ceiling(ylim[2L] / yg2) * yg2, by = yg2) } abline(h = yg, col = gcol, lty = glty, lwd = glwd) gny = NA @@ -571,8 +568,7 @@ draw_facet_window = function( yg = if (!inherits(y, c("POSIXt", "Date"))) axTicks(side = 2) else axTicksDateTime(side = 2, x = x) if (fine_y && length(yg) >= 2L) { yg2 = (yg[2L] - yg[1L]) / 2 - yg = seq(yg[1L] - yg2, yg[length(yg)] + yg2, by = yg2) - yg = yg[yg >= ylim[1L] & yg <= ylim[2L]] + yg = seq(floor(ylim[1L] / yg2) * yg2, ceiling(ylim[2L] / yg2) * yg2, by = yg2) } abline(h = yg, col = gcol, lty = glty, lwd = glwd) gny = NA From de980ac1ed39b0d9cfad8d8016f9d8350f343333 Mon Sep 17 00:00:00 2001 From: Grant McDermott Date: Thu, 7 May 2026 17:40:40 -0700 Subject: [PATCH 03/13] news --- NEWS.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/NEWS.md b/NEWS.md index 0b8df3e2..c52308ba 100644 --- a/NEWS.md +++ b/NEWS.md @@ -32,6 +32,12 @@ where the formatting is also better._ ### New features +- The `grid` argument (and `tpar("grid")`) now accepts character strings to + control axis-specific grids at different resolutions. Uppercase letters + (`"X"`, `"Y"`, `"XY"`) draw grid lines at the standard tick positions, while + lowercase letters (`"x"`, `"y"`, `"xy"`) draw a finer grid with additional + lines at the midpoints between ticks. Thanks to @zeileis for the suggestion. + (#578 @grantmcdermott) - New `"dynamic"` theme that now serves as the foundation for all other dynamic (tiny)themes. (#549 @grantmcdermott) From 242bea0838eb90cdf706023b63162d07bf9335ab Mon Sep 17 00:00:00 2001 From: Grant McDermott Date: Fri, 8 May 2026 09:21:29 -0700 Subject: [PATCH 04/13] log and datetime gotchas --- R/facet.R | 45 ++++++++++++++------------------------------- 1 file changed, 14 insertions(+), 31 deletions(-) diff --git a/R/facet.R b/R/facet.R index 71596648..233a9b63 100644 --- a/R/facet.R +++ b/R/facet.R @@ -533,52 +533,35 @@ draw_facet_window = function( } if (draw_x || draw_y) { - gnx = gny = NULL if (draw_x) { if (!is.null(xaxb)) { xg = xaxb - if (fine_x && length(xg) >= 2L) { - xg2 = (xg[2L] - xg[1L]) / 2 - xg = seq(floor(xlim[1L] / xg2) * xg2, ceiling(xlim[2L] / xg2) * xg2, by = xg2) - } - abline(v = xg, col = gcol, lty = glty, lwd = glwd) - gnx = NA - } else if (!any(c(par("xlog"), type == "boxplot"))) { + } else { xg = if (!inherits(x, c("POSIXt", "Date"))) axTicks(side = 1) else axTicksDateTime(side = 1, x = x) - if (fine_x && length(xg) >= 2L) { - xg2 = (xg[2L] - xg[1L]) / 2 - xg = seq(floor(xlim[1L] / xg2) * xg2, ceiling(xlim[2L] / xg2) * xg2, by = xg2) - } - abline(v = xg, col = gcol, lty = glty, lwd = glwd) - gnx = NA } + if (fine_x && !par("xlog") && length(xg) >= 2L) { + xg = as.numeric(xg) + mids = (xg[-length(xg)] + xg[-1L]) / 2 + xg = sort(c(xg, mids)) + } + abline(v = xg, col = gcol, lty = glty, lwd = glwd) } if (draw_y) { if (!is.null(yaxb)) { yg = yaxb - if (fine_y && length(yg) >= 2L) { - yg2 = (yg[2L] - yg[1L]) / 2 - yg = seq(floor(ylim[1L] / yg2) * yg2, ceiling(ylim[2L] / yg2) * yg2, by = yg2) - } - abline(h = yg, col = gcol, lty = glty, lwd = glwd) - gny = NA - } else if (!any(c(par("ylog"), type == "boxplot"))) { + } else { yg = if (!inherits(y, c("POSIXt", "Date"))) axTicks(side = 2) else axTicksDateTime(side = 2, x = x) - if (fine_y && length(yg) >= 2L) { - yg2 = (yg[2L] - yg[1L]) / 2 - yg = seq(floor(ylim[1L] / yg2) * yg2, ceiling(ylim[2L] / yg2) * yg2, by = yg2) - } - abline(h = yg, col = gcol, lty = glty, lwd = glwd) - gny = NA } + if (fine_y && !par("ylog") && length(yg) >= 2L) { + yg = as.numeric(yg) + mids = (yg[-length(yg)] + yg[-1L]) / 2 + yg = sort(c(yg, mids)) + } + abline(h = yg, col = gcol, lty = glty, lwd = glwd) } - # Handle log-scale axes via grid() for any axes not yet drawn - grid(nx = gnx %||% if (draw_x) NULL else NA, - ny = gny %||% if (draw_y) NULL else NA, - col = gcol, lty = glty, lwd = glwd) } } From 66465e5a3ab2bc14632337270d6089f2f18dc785 Mon Sep 17 00:00:00 2001 From: Grant McDermott Date: Fri, 8 May 2026 09:21:42 -0700 Subject: [PATCH 05/13] tests --- .../tinytest/_tinysnapshot/grid_date_true.svg | 197 +++++++++++++++++ inst/tinytest/_tinysnapshot/grid_date_xy.svg | 206 ++++++++++++++++++ inst/tinytest/_tinysnapshot/grid_facet_xy.svg | 114 ++++++++++ inst/tinytest/_tinysnapshot/grid_log_true.svg | 186 ++++++++++++++++ inst/tinytest/_tinysnapshot/grid_log_xy.svg | 186 ++++++++++++++++ inst/tinytest/_tinysnapshot/grid_logx_x.svg | 177 +++++++++++++++ .../tinytest/_tinysnapshot/grid_numeric_Y.svg | 98 +++++++++ .../_tinysnapshot/grid_numeric_false.svg | 94 ++++++++ .../_tinysnapshot/grid_numeric_true.svg | 103 +++++++++ .../_tinysnapshot/grid_numeric_upper_xy.svg | 103 +++++++++ .../tinytest/_tinysnapshot/grid_numeric_x.svg | 103 +++++++++ .../_tinysnapshot/grid_numeric_xy.svg | 110 ++++++++++ inst/tinytest/_tinysnapshot/grid_theme_xy.svg | 110 ++++++++++ inst/tinytest/test-grid.R | 67 ++++++ 14 files changed, 1854 insertions(+) create mode 100644 inst/tinytest/_tinysnapshot/grid_date_true.svg create mode 100644 inst/tinytest/_tinysnapshot/grid_date_xy.svg create mode 100644 inst/tinytest/_tinysnapshot/grid_facet_xy.svg create mode 100644 inst/tinytest/_tinysnapshot/grid_log_true.svg create mode 100644 inst/tinytest/_tinysnapshot/grid_log_xy.svg create mode 100644 inst/tinytest/_tinysnapshot/grid_logx_x.svg create mode 100644 inst/tinytest/_tinysnapshot/grid_numeric_Y.svg create mode 100644 inst/tinytest/_tinysnapshot/grid_numeric_false.svg create mode 100644 inst/tinytest/_tinysnapshot/grid_numeric_true.svg create mode 100644 inst/tinytest/_tinysnapshot/grid_numeric_upper_xy.svg create mode 100644 inst/tinytest/_tinysnapshot/grid_numeric_x.svg create mode 100644 inst/tinytest/_tinysnapshot/grid_numeric_xy.svg create mode 100644 inst/tinytest/_tinysnapshot/grid_theme_xy.svg create mode 100644 inst/tinytest/test-grid.R diff --git a/inst/tinytest/_tinysnapshot/grid_date_true.svg b/inst/tinytest/_tinysnapshot/grid_date_true.svg new file mode 100644 index 00000000..ea0d6dc7 --- /dev/null +++ b/inst/tinytest/_tinysnapshot/grid_date_true.svg @@ -0,0 +1,197 @@ + + + + + + + + + + + + + +date: grid = TRUE +ds +y + + + + + + +2016 +2018 +2020 +2022 +2024 + + + + + + + +-2 +0 +2 +4 +6 +8 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inst/tinytest/_tinysnapshot/grid_date_xy.svg b/inst/tinytest/_tinysnapshot/grid_date_xy.svg new file mode 100644 index 00000000..af916754 --- /dev/null +++ b/inst/tinytest/_tinysnapshot/grid_date_xy.svg @@ -0,0 +1,206 @@ + + + + + + + + + + + + + +date: grid = "xy" +ds +y + + + + + + +2016 +2018 +2020 +2022 +2024 + + + + + + + +-2 +0 +2 +4 +6 +8 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inst/tinytest/_tinysnapshot/grid_facet_xy.svg b/inst/tinytest/_tinysnapshot/grid_facet_xy.svg new file mode 100644 index 00000000..3c829dbe --- /dev/null +++ b/inst/tinytest/_tinysnapshot/grid_facet_xy.svg @@ -0,0 +1,114 @@ + + + + + + + + + + + + + + + + +factor(cyl) +4 +6 +8 + + + + + + + +faceted: grid = "xy" +mpg +wt + + +10 +15 +20 +25 +30 +2 +3 +4 +5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inst/tinytest/_tinysnapshot/grid_log_true.svg b/inst/tinytest/_tinysnapshot/grid_log_true.svg new file mode 100644 index 00000000..4f2e0981 --- /dev/null +++ b/inst/tinytest/_tinysnapshot/grid_log_true.svg @@ -0,0 +1,186 @@ + + + + + + + + + + + + + +log: grid = TRUE +Index +1:100 + + + + + + + + +1 +2 +5 +10 +20 +50 +100 + + + + + + + + +1 +2 +5 +10 +20 +50 +100 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inst/tinytest/_tinysnapshot/grid_log_xy.svg b/inst/tinytest/_tinysnapshot/grid_log_xy.svg new file mode 100644 index 00000000..87a4835b --- /dev/null +++ b/inst/tinytest/_tinysnapshot/grid_log_xy.svg @@ -0,0 +1,186 @@ + + + + + + + + + + + + + +log: grid = "xy" +Index +1:100 + + + + + + + + +1 +2 +5 +10 +20 +50 +100 + + + + + + + + +1 +2 +5 +10 +20 +50 +100 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inst/tinytest/_tinysnapshot/grid_logx_x.svg b/inst/tinytest/_tinysnapshot/grid_logx_x.svg new file mode 100644 index 00000000..3ebd941c --- /dev/null +++ b/inst/tinytest/_tinysnapshot/grid_logx_x.svg @@ -0,0 +1,177 @@ + + + + + + + + + + + + + +log x: grid = "x" +Index +1:100 + + + + + + + + +1 +2 +5 +10 +20 +50 +100 + + + + + + + +0 +20 +40 +60 +80 +100 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inst/tinytest/_tinysnapshot/grid_numeric_Y.svg b/inst/tinytest/_tinysnapshot/grid_numeric_Y.svg new file mode 100644 index 00000000..b4b8cdba --- /dev/null +++ b/inst/tinytest/_tinysnapshot/grid_numeric_Y.svg @@ -0,0 +1,98 @@ + + + + + + + + + + + + + +numeric: grid = "Y" +mpg +wt + + + + + + +10 +15 +20 +25 +30 + + + + + +2 +3 +4 +5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inst/tinytest/_tinysnapshot/grid_numeric_false.svg b/inst/tinytest/_tinysnapshot/grid_numeric_false.svg new file mode 100644 index 00000000..38f7f303 --- /dev/null +++ b/inst/tinytest/_tinysnapshot/grid_numeric_false.svg @@ -0,0 +1,94 @@ + + + + + + + + + + + + + +numeric: grid = FALSE +mpg +wt + + + + + + +10 +15 +20 +25 +30 + + + + + +2 +3 +4 +5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inst/tinytest/_tinysnapshot/grid_numeric_true.svg b/inst/tinytest/_tinysnapshot/grid_numeric_true.svg new file mode 100644 index 00000000..f0a352fd --- /dev/null +++ b/inst/tinytest/_tinysnapshot/grid_numeric_true.svg @@ -0,0 +1,103 @@ + + + + + + + + + + + + + +numeric: grid = TRUE +mpg +wt + + + + + + +10 +15 +20 +25 +30 + + + + + +2 +3 +4 +5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inst/tinytest/_tinysnapshot/grid_numeric_upper_xy.svg b/inst/tinytest/_tinysnapshot/grid_numeric_upper_xy.svg new file mode 100644 index 00000000..d851b4b0 --- /dev/null +++ b/inst/tinytest/_tinysnapshot/grid_numeric_upper_xy.svg @@ -0,0 +1,103 @@ + + + + + + + + + + + + + +numeric: grid = "XY" +mpg +wt + + + + + + +10 +15 +20 +25 +30 + + + + + +2 +3 +4 +5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inst/tinytest/_tinysnapshot/grid_numeric_x.svg b/inst/tinytest/_tinysnapshot/grid_numeric_x.svg new file mode 100644 index 00000000..8dd4cebd --- /dev/null +++ b/inst/tinytest/_tinysnapshot/grid_numeric_x.svg @@ -0,0 +1,103 @@ + + + + + + + + + + + + + +numeric: grid = "x" +mpg +wt + + + + + + +10 +15 +20 +25 +30 + + + + + +2 +3 +4 +5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inst/tinytest/_tinysnapshot/grid_numeric_xy.svg b/inst/tinytest/_tinysnapshot/grid_numeric_xy.svg new file mode 100644 index 00000000..853c5ede --- /dev/null +++ b/inst/tinytest/_tinysnapshot/grid_numeric_xy.svg @@ -0,0 +1,110 @@ + + + + + + + + + + + + + +numeric: grid = "xy" +mpg +wt + + + + + + +10 +15 +20 +25 +30 + + + + + +2 +3 +4 +5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inst/tinytest/_tinysnapshot/grid_theme_xy.svg b/inst/tinytest/_tinysnapshot/grid_theme_xy.svg new file mode 100644 index 00000000..50a3133c --- /dev/null +++ b/inst/tinytest/_tinysnapshot/grid_theme_xy.svg @@ -0,0 +1,110 @@ + + + + + + + + + + + + + +theme: grid = "xy" +mpg +wt + + + + + + +10 +15 +20 +25 +30 + + + + + +2 +3 +4 +5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inst/tinytest/test-grid.R b/inst/tinytest/test-grid.R new file mode 100644 index 00000000..3fcffb40 --- /dev/null +++ b/inst/tinytest/test-grid.R @@ -0,0 +1,67 @@ +source("helpers.R") +using("tinysnapshot") + +set.seed(42) +dat = data.frame( + y = cumsum(rnorm(120)), + ds = seq(as.Date("2015-01-01"), as.Date("2024-12-01"), by = "month") +) + +# Numeric axes + +f = function() plt(wt ~ mpg, mtcars, grid = TRUE, main = "numeric: grid = TRUE") +expect_snapshot_plot(f, label = "grid_numeric_true") + +f = function() plt(wt ~ mpg, mtcars, grid = "XY", main = "numeric: grid = \"XY\"") +expect_snapshot_plot(f, label = "grid_numeric_upper_xy") + +f = function() plt(wt ~ mpg, mtcars, grid = "xy", main = "numeric: grid = \"xy\"") +expect_snapshot_plot(f, label = "grid_numeric_xy") + +f = function() plt(wt ~ mpg, mtcars, grid = "x", main = "numeric: grid = \"x\"") +expect_snapshot_plot(f, label = "grid_numeric_x") + +f = function() plt(wt ~ mpg, mtcars, grid = "Y", main = "numeric: grid = \"Y\"") +expect_snapshot_plot(f, label = "grid_numeric_Y") + +f = function() plt(wt ~ mpg, mtcars, grid = FALSE, main = "numeric: grid = FALSE") +expect_snapshot_plot(f, label = "grid_numeric_false") + +# Date axes + +f = function() plt(y ~ ds, dat, grid = TRUE, main = "date: grid = TRUE") +expect_snapshot_plot(f, label = "grid_date_true") + +f = function() plt(y ~ ds, dat, grid = "xy", main = "date: grid = \"xy\"") +expect_snapshot_plot(f, label = "grid_date_xy") + +# Log axes + +f = function() plt(1:100, log = "xy", grid = TRUE, main = "log: grid = TRUE") +expect_snapshot_plot(f, label = "grid_log_true") + +f = function() plt(1:100, log = "xy", grid = "xy", main = "log: grid = \"xy\"") +expect_snapshot_plot(f, label = "grid_log_xy") + +# Single axis on log + +f = function() plt(1:100, log = "x", grid = "x", main = "log x: grid = \"x\"") +expect_snapshot_plot(f, label = "grid_logx_x") + +# Faceted + +f = function() { + tinytheme("clean2", grid = "xy") + plt(wt ~ mpg | factor(cyl), mtcars, main = "faceted: grid = \"xy\"") + tinytheme() +} +expect_snapshot_plot(f, label = "grid_facet_xy") + +# Via theme + +f = function() { + tinytheme("clean", grid = "xy") + plt(wt ~ mpg, mtcars, main = "theme: grid = \"xy\"") + tinytheme() +} +expect_snapshot_plot(f, label = "grid_theme_xy") From b57a85e97fec68d6aeda4ddc674965ec7693e038 Mon Sep 17 00:00:00 2001 From: Grant McDermott Date: Fri, 8 May 2026 09:30:38 -0700 Subject: [PATCH 06/13] update fp snapshots --- inst/tinytest/_tinysnapshot/flip_boxplot.svg | 12 ++++++------ .../_tinysnapshot/tinytheme_dynamic_boxplot_flip.svg | 12 ++++++------ .../_tinysnapshot/tinytheme_dynamic_x_boxplot.svg | 12 ++++++------ 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/inst/tinytest/_tinysnapshot/flip_boxplot.svg b/inst/tinytest/_tinysnapshot/flip_boxplot.svg index 9718c6cb..15521167 100644 --- a/inst/tinytest/_tinysnapshot/flip_boxplot.svg +++ b/inst/tinytest/_tinysnapshot/flip_boxplot.svg @@ -62,18 +62,18 @@ - - - - - - + + + + + + diff --git a/inst/tinytest/_tinysnapshot/tinytheme_dynamic_boxplot_flip.svg b/inst/tinytest/_tinysnapshot/tinytheme_dynamic_boxplot_flip.svg index 79bdee34..d607a6c3 100644 --- a/inst/tinytest/_tinysnapshot/tinytheme_dynamic_boxplot_flip.svg +++ b/inst/tinytest/_tinysnapshot/tinytheme_dynamic_boxplot_flip.svg @@ -66,12 +66,6 @@ - - - - - - @@ -79,6 +73,12 @@ + + + + + + diff --git a/inst/tinytest/_tinysnapshot/tinytheme_dynamic_x_boxplot.svg b/inst/tinytest/_tinysnapshot/tinytheme_dynamic_x_boxplot.svg index bcf23ce9..d538e0c9 100644 --- a/inst/tinytest/_tinysnapshot/tinytheme_dynamic_x_boxplot.svg +++ b/inst/tinytest/_tinysnapshot/tinytheme_dynamic_x_boxplot.svg @@ -66,6 +66,12 @@ + + + + + + @@ -73,12 +79,6 @@ - - - - - - From da1283b3c4df323ef233fe8406ca5e97ae3cae91 Mon Sep 17 00:00:00 2001 From: Grant McDermott Date: Fri, 8 May 2026 10:35:07 -0700 Subject: [PATCH 07/13] catch edge lines --- R/facet.R | 6 ++++-- inst/tinytest/_tinysnapshot/grid_date_xy.svg | 4 ++++ inst/tinytest/_tinysnapshot/grid_facet_xy.svg | 4 ++++ inst/tinytest/_tinysnapshot/grid_numeric_x.svg | 2 ++ inst/tinytest/_tinysnapshot/grid_numeric_xy.svg | 4 ++++ inst/tinytest/_tinysnapshot/grid_theme_xy.svg | 4 ++++ 6 files changed, 22 insertions(+), 2 deletions(-) diff --git a/R/facet.R b/R/facet.R index 233a9b63..af88665d 100644 --- a/R/facet.R +++ b/R/facet.R @@ -542,8 +542,9 @@ draw_facet_window = function( } if (fine_x && !par("xlog") && length(xg) >= 2L) { xg = as.numeric(xg) + half = (xg[2L] - xg[1L]) / 2 mids = (xg[-length(xg)] + xg[-1L]) / 2 - xg = sort(c(xg, mids)) + xg = sort(c(xg[1L] - half, xg, mids, xg[length(xg)] + half)) } abline(v = xg, col = gcol, lty = glty, lwd = glwd) } @@ -556,8 +557,9 @@ draw_facet_window = function( } if (fine_y && !par("ylog") && length(yg) >= 2L) { yg = as.numeric(yg) + half = (yg[2L] - yg[1L]) / 2 mids = (yg[-length(yg)] + yg[-1L]) / 2 - yg = sort(c(yg, mids)) + yg = sort(c(yg[1L] - half, yg, mids, yg[length(yg)] + half)) } abline(h = yg, col = gcol, lty = glty, lwd = glwd) } diff --git a/inst/tinytest/_tinysnapshot/grid_date_xy.svg b/inst/tinytest/_tinysnapshot/grid_date_xy.svg index af916754..fd174201 100644 --- a/inst/tinytest/_tinysnapshot/grid_date_xy.svg +++ b/inst/tinytest/_tinysnapshot/grid_date_xy.svg @@ -61,6 +61,7 @@ + @@ -70,6 +71,8 @@ + + @@ -81,6 +84,7 @@ + diff --git a/inst/tinytest/_tinysnapshot/grid_facet_xy.svg b/inst/tinytest/_tinysnapshot/grid_facet_xy.svg index 3c829dbe..3393dd1b 100644 --- a/inst/tinytest/_tinysnapshot/grid_facet_xy.svg +++ b/inst/tinytest/_tinysnapshot/grid_facet_xy.svg @@ -61,6 +61,7 @@ + @@ -70,6 +71,8 @@ + + @@ -77,6 +80,7 @@ + diff --git a/inst/tinytest/_tinysnapshot/grid_numeric_x.svg b/inst/tinytest/_tinysnapshot/grid_numeric_x.svg index 8dd4cebd..30eccf76 100644 --- a/inst/tinytest/_tinysnapshot/grid_numeric_x.svg +++ b/inst/tinytest/_tinysnapshot/grid_numeric_x.svg @@ -57,6 +57,7 @@ + @@ -66,6 +67,7 @@ + diff --git a/inst/tinytest/_tinysnapshot/grid_numeric_xy.svg b/inst/tinytest/_tinysnapshot/grid_numeric_xy.svg index 853c5ede..9a020c14 100644 --- a/inst/tinytest/_tinysnapshot/grid_numeric_xy.svg +++ b/inst/tinytest/_tinysnapshot/grid_numeric_xy.svg @@ -57,6 +57,7 @@ + @@ -66,6 +67,8 @@ + + @@ -73,6 +76,7 @@ + diff --git a/inst/tinytest/_tinysnapshot/grid_theme_xy.svg b/inst/tinytest/_tinysnapshot/grid_theme_xy.svg index 50a3133c..c5f96b75 100644 --- a/inst/tinytest/_tinysnapshot/grid_theme_xy.svg +++ b/inst/tinytest/_tinysnapshot/grid_theme_xy.svg @@ -57,6 +57,7 @@ + @@ -66,6 +67,8 @@ + + @@ -73,6 +76,7 @@ + From 9556abc7a4f3d4b61e63012ccb375e5fdd4fa40c Mon Sep 17 00:00:00 2001 From: Grant McDermott Date: Fri, 8 May 2026 10:41:15 -0700 Subject: [PATCH 08/13] simpify --- R/facet.R | 6 ++---- inst/tinytest/_tinysnapshot/grid_date_xy.svg | 13 ++++++------- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/R/facet.R b/R/facet.R index af88665d..9a9d2424 100644 --- a/R/facet.R +++ b/R/facet.R @@ -543,8 +543,7 @@ draw_facet_window = function( if (fine_x && !par("xlog") && length(xg) >= 2L) { xg = as.numeric(xg) half = (xg[2L] - xg[1L]) / 2 - mids = (xg[-length(xg)] + xg[-1L]) / 2 - xg = sort(c(xg[1L] - half, xg, mids, xg[length(xg)] + half)) + xg = seq(xg[1L] - half, xg[length(xg)] + half, by = half) } abline(v = xg, col = gcol, lty = glty, lwd = glwd) } @@ -558,8 +557,7 @@ draw_facet_window = function( if (fine_y && !par("ylog") && length(yg) >= 2L) { yg = as.numeric(yg) half = (yg[2L] - yg[1L]) / 2 - mids = (yg[-length(yg)] + yg[-1L]) / 2 - yg = sort(c(yg[1L] - half, yg, mids, yg[length(yg)] + half)) + yg = seq(yg[1L] - half, yg[length(yg)] + half, by = half) } abline(h = yg, col = gcol, lty = glty, lwd = glwd) } diff --git a/inst/tinytest/_tinysnapshot/grid_date_xy.svg b/inst/tinytest/_tinysnapshot/grid_date_xy.svg index fd174201..6f8b047b 100644 --- a/inst/tinytest/_tinysnapshot/grid_date_xy.svg +++ b/inst/tinytest/_tinysnapshot/grid_date_xy.svg @@ -65,13 +65,12 @@ - - - - - - - + + + + + + From f5f895e46263ca58de5f79cfef683520a13ad54c Mon Sep 17 00:00:00 2001 From: Grant McDermott Date: Fri, 8 May 2026 10:56:00 -0700 Subject: [PATCH 09/13] note (deliberate) differential log behaviour for finer grid --- R/tinyplot.R | 6 ++++-- man/tinyplot.Rd | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/R/tinyplot.R b/R/tinyplot.R index dedced43..70a8506f 100644 --- a/R/tinyplot.R +++ b/R/tinyplot.R @@ -198,12 +198,14 @@ #' The default is to draw a frame if both axis types (set via `axes`, `xaxt`, #' or `yaxt`) include axis lines. #' @param grid argument for plotting a background panel grid, one of: -#' - a logical (i.e., `TRUE` to draw the grid on both axes), +#' - a logical (i.e., `TRUE` to draw the grid on both axes). #' - a character string controlling which axes get grid lines and at what #' resolution. Uppercase letters (`"X"`, `"Y"`, `"XY"`) draw grid lines at #' the standard axis tick positions (with the latter equivalent to `TRUE`), #' while lowercase letters (`"x"`, `"y"`, `"xy"`) draw a finer grid with -#' additional lines at the midpoints between ticks, or +#' additional lines at the midpoints between ticks. (Note: the finer grid +#' has no effect on log-scale axes, since tick positions already include +#' intermediate values.) #' - a panel grid plotting function like `grid()`. #' Note that this argument replaces the `panel.first` and `panel.last` #' arguments from base `plot()` and tries to make the process more seamless diff --git a/man/tinyplot.Rd b/man/tinyplot.Rd index ccc19b8a..339dc7ce 100644 --- a/man/tinyplot.Rd +++ b/man/tinyplot.Rd @@ -333,12 +333,14 @@ or \code{yaxt}) include axis lines.} \item{grid}{argument for plotting a background panel grid, one of: \itemize{ -\item a logical (i.e., \code{TRUE} to draw the grid on both axes), +\item a logical (i.e., \code{TRUE} to draw the grid on both axes). \item a character string controlling which axes get grid lines and at what resolution. Uppercase letters (\code{"X"}, \code{"Y"}, \code{"XY"}) draw grid lines at the standard axis tick positions (with the latter equivalent to \code{TRUE}), while lowercase letters (\code{"x"}, \code{"y"}, \code{"xy"}) draw a finer grid with -additional lines at the midpoints between ticks, or +additional lines at the midpoints between ticks. (Note: the finer grid +has no effect on log-scale axes, since tick positions already include +intermediate values.) \item a panel grid plotting function like \code{grid()}. Note that this argument replaces the \code{panel.first} and \code{panel.last} arguments from base \code{plot()} and tries to make the process more seamless From e055fce0cfd63c1ce54c9e793d7ac394fb39728b Mon Sep 17 00:00:00 2001 From: Grant McDermott Date: Fri, 8 May 2026 11:24:55 -0700 Subject: [PATCH 10/13] better assertions --- R/assertions.R | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/R/assertions.R b/R/assertions.R index 1b411daa..a2231abc 100644 --- a/R/assertions.R +++ b/R/assertions.R @@ -93,9 +93,9 @@ assert_length = function(x, len = 1, null.ok = FALSE, name = as.character(substi assert_grid = function(x, null.ok = FALSE, name = as.character(substitute(x))) { if (is.null(x) && isTRUE(null.ok)) return(invisible(TRUE)) if (is.logical(x) && length(x) == 1) return(invisible(TRUE)) - valid_strings = c("x", "y", "xy", "X", "Y", "XY") + valid_strings = c("x", "y", "X", "Y", "xy", "xY", "Xy", "XY") if (is.character(x) && length(x) == 1 && x %in% valid_strings) return(invisible(TRUE)) - msg = sprintf("`%s` must be a logical flag or one of: %s", name, paste(valid_strings, collapse = ", ")) + msg = sprintf("`%s` must be a logical flag or a character string combining x/X and/or y/Y (e.g., \"xy\", \"XY\", \"xY\")", name) stop(msg, call. = FALSE) } From f7a5c6f8c1491c3669d6a330298241600e7db432 Mon Sep 17 00:00:00 2001 From: Grant McDermott Date: Fri, 8 May 2026 11:33:12 -0700 Subject: [PATCH 11/13] issue #193 --- R/facet.R | 2 +- R/tinyplot.R | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/R/facet.R b/R/facet.R index 9a9d2424..32eabe69 100644 --- a/R/facet.R +++ b/R/facet.R @@ -528,7 +528,7 @@ draw_facet_window = function( fine_x = grepl("x", grid, fixed = TRUE) fine_y = grepl("y", grid, fixed = TRUE) } else { - grid + eval(grid) # issue #193 draw_x = draw_y = FALSE } diff --git a/R/tinyplot.R b/R/tinyplot.R index 70a8506f..f21f6356 100644 --- a/R/tinyplot.R +++ b/R/tinyplot.R @@ -660,6 +660,12 @@ tinyplot.default = function( par_first = get_saved_par("first") if (is.null(par_first)) set_saved_par("first", par()) + # Validate grid only for simple values; skip for unevaluated calls like grid() + # which are passed as language objects from tinyplot.formula via substitute(). (#193) + if (!is.null(grid) && !is.call(grid)) { + assert_grid(grid, null.ok = TRUE, name = "grid") + } + # save for tinyplot_add() assert_logical(add) if (!add) { @@ -1587,7 +1593,7 @@ tinyplot.formula = function( axes = axes, frame.plot = frame.plot, asp = asp, - grid = grid, + grid = substitute(grid), # issue #193 legend_args = legend_args, pch = pch, col = col, From a0eef0f903c04f2168d1a086b1281010358e4e9d Mon Sep 17 00:00:00 2001 From: Grant McDermott Date: Fri, 8 May 2026 11:33:20 -0700 Subject: [PATCH 12/13] moar tests --- .../_tinysnapshot/grid_func_facet.svg | 395 ++++++++++++++++++ inst/tinytest/test-grid.R | 17 + 2 files changed, 412 insertions(+) create mode 100644 inst/tinytest/_tinysnapshot/grid_func_facet.svg diff --git a/inst/tinytest/_tinysnapshot/grid_func_facet.svg b/inst/tinytest/_tinysnapshot/grid_func_facet.svg new file mode 100644 index 00000000..5f654928 --- /dev/null +++ b/inst/tinytest/_tinysnapshot/grid_func_facet.svg @@ -0,0 +1,395 @@ + + + + + + + + + + + + + + + 5 + 6 + 7 +- - +- - +- - +Sepal.Length + + + + + + + +grid = grid() +Petal.Length +Sepal.Length + + + + + + + + + + + + + + + + + +1 +2 +3 +4 +5 +6 +7 + + + + + + + + + +4.5 +5.0 +5.5 +6.0 +6.5 +7.0 +7.5 +8.0 + +setosa + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +1 +2 +3 +4 +5 +6 +7 + + + + + + + + + +4.5 +5.0 +5.5 +6.0 +6.5 +7.0 +7.5 +8.0 + +versicolor + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +1 +2 +3 +4 +5 +6 +7 + + + + + + + + + +4.5 +5.0 +5.5 +6.0 +6.5 +7.0 +7.5 +8.0 + +virginica + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inst/tinytest/test-grid.R b/inst/tinytest/test-grid.R index 3fcffb40..c320966e 100644 --- a/inst/tinytest/test-grid.R +++ b/inst/tinytest/test-grid.R @@ -65,3 +65,20 @@ f = function() { tinytheme() } expect_snapshot_plot(f, label = "grid_theme_xy") + +# grid = grid() with facets (issue #193) + +f = function() { + plt( + Sepal.Length ~ Petal.Length | Sepal.Length, data = iris, + facet = ~Species, pch = 19, + grid = grid(), + main = "grid = grid()" + ) +} +expect_snapshot_plot(f, label = "grid_func_facet") + +# Input validation + +expect_error(plt(wt ~ mpg, mtcars, grid = "zz")) +expect_error(plt(wt ~ mpg, mtcars, grid = "abc")) From 73ccc3fdaeb80072936b1af8a5d44c91454a146b Mon Sep 17 00:00:00 2001 From: Grant McDermott Date: Fri, 8 May 2026 11:46:22 -0700 Subject: [PATCH 13/13] news --- NEWS.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/NEWS.md b/NEWS.md index c52308ba..96267a8f 100644 --- a/NEWS.md +++ b/NEWS.md @@ -41,6 +41,13 @@ where the formatting is also better._ - New `"dynamic"` theme that now serves as the foundation for all other dynamic (tiny)themes. (#549 @grantmcdermott) +### Bug fixes + +- Fixed `grid = grid()` not drawing grid lines on all facets. That said, logical + or character inputs (e.g., `grid = TRUE`, `grid = "xy"`) remain the more + idiomatic way to generate a background grid in `tinyplot`. + (#193 @grantmcdermott) + ## v0.6.1 ### Aesthetic changes