From 6c0be52eb7a8de0590c590da31da58e4fe4dfe65 Mon Sep 17 00:00:00 2001 From: Grant McDermott Date: Wed, 16 Sep 2026 10:19:25 -0700 Subject: [PATCH 1/7] fix straight line flipping --- R/type_abline.R | 29 +++++++++++++++++++++++++++-- R/type_hline.R | 9 +++++++-- R/type_vline.R | 9 +++++++-- man/type_abline.Rd | 15 +++++++++++++++ 4 files changed, 56 insertions(+), 6 deletions(-) diff --git a/R/type_abline.R b/R/type_abline.R index 9d95741ad..ae7e8e2a4 100644 --- a/R/type_abline.R +++ b/R/type_abline.R @@ -5,6 +5,11 @@ #' While `type_abline`, `type_hline`, and `type_vline` can be called in a base #' plot layer, we expect that they will typically be called as subsequent #' layers via [`tinyplot_add`]. +#' +#' Line parameters always refer to the original (pre-flip) variables. So, if +#' `flip = TRUE`, then `type_hline()` draws vertical line(s) at the given `y` +#' value(s), `type_vline()` draws horizontal line(s) at the given `x` value(s), +#' and `type_abline()` draws the line \eqn{y = a + bx} with the axes swapped. #' @section Recycling logic: #' The recycling behaviour of the line parameters (i.e., `a`, `b`, `h`, or `v`) #' is adaptive, depending on whether `by` or `facet` grouping is detected. While @@ -72,6 +77,16 @@ #' tinyplot_add(type = type_vline(with(mtcars, tapply(hp, cyl, mean))), lty = 2) #' #' # +#' ## Flipped plots +#' +#' # Line parameters refer to the original x and y variables, so "hline" is +#' # drawn vertically and "vline" horizontally when flip = TRUE +#' tinyplot(mpg ~ wt, data = mtcars, flip = TRUE) +#' tinyplot_add(type = type_hline(20), col = "hotpink") +#' tinyplot_add(type = type_vline(3), col = "dodgerblue") +#' tinyplot_add(type = type_abline(a = 37, b = -5), lty = 2) +#' +#' # #' ## Recycling logic #' #' # length(h) == no. of groups @@ -109,7 +124,7 @@ type_abline = function(a = 0, b = 1) { draw_abline = function() { fun = function(ifacet, iby, data_facet, icol, ilty, ilwd, ngrps, nfacets, by_continuous, facet_by, - type_info, + type_info, flip = FALSE, ...) { # flag for aesthetics by groups grp_aes = type_info[["ul_col"]] == 1 || type_info[["ul_lty"]] == ngrps || type_info[["ul_lwd"]] == ngrps @@ -160,7 +175,17 @@ type_abline = function(a = 0, b = 1) { icol = 1 } - abline(a = a, b = b, col = icol, lty = ilty, lwd = ilwd) + # under flip, y = a + b*x is drawn with the axes swapped, i.e. as + # x = (y - a) / b in plotting coordinates (vertical line if b == 0) + if (isTRUE(flip)) { + if (b == 0) { + abline(v = a, col = icol, lty = ilty, lwd = ilwd) + } else { + abline(a = -a / b, b = 1 / b, col = icol, lty = ilty, lwd = ilwd) + } + } else { + abline(a = a, b = b, col = icol, lty = ilty, lwd = ilwd) + } } return(fun) } diff --git a/R/type_hline.R b/R/type_hline.R index 3ac9becee..1c8d419a3 100644 --- a/R/type_hline.R +++ b/R/type_hline.R @@ -22,7 +22,7 @@ type_hline = function(h = 0) { draw_hline = function() { fun = function(ifacet, iby, data_facet, icol, ilty, ilwd, ngrps, nfacets, by_continuous, facet_by, - type_info, + type_info, flip = FALSE, ...) { # flag for aesthetics by groups grp_aes = type_info[["ul_col"]] == 1 || type_info[["ul_lty"]] == ngrps || type_info[["ul_lwd"]] == ngrps @@ -48,7 +48,12 @@ type_hline = function(h = 0) { icol = 1 } - abline(h = h, col = icol, lty = ilty, lwd = ilwd) + # under flip, the y variable runs along the horizontal axis + if (isTRUE(flip)) { + abline(v = h, col = icol, lty = ilty, lwd = ilwd) + } else { + abline(h = h, col = icol, lty = ilty, lwd = ilwd) + } } return(fun) } diff --git a/R/type_vline.R b/R/type_vline.R index 25748211c..a6b3d0331 100644 --- a/R/type_vline.R +++ b/R/type_vline.R @@ -22,7 +22,7 @@ type_vline = function(v = 0) { draw_vline = function() { fun = function(ifacet, iby, data_facet, icol, ilty, ilwd, ngrps, nfacets, by_continuous, facet_by, - type_info, + type_info, flip = FALSE, ...) { # flag for aesthetics by groups grp_aes = type_info[["ul_col"]] == 1 || type_info[["ul_lty"]] == ngrps || type_info[["ul_lwd"]] == ngrps @@ -48,7 +48,12 @@ type_vline = function(v = 0) { icol = 1 } - abline(v = v, col = icol, lty = ilty, lwd = ilwd) + # under flip, the x variable runs along the vertical axis + if (isTRUE(flip)) { + abline(h = v, col = icol, lty = ilty, lwd = ilwd) + } else { + abline(v = v, col = icol, lty = ilty, lwd = ilwd) + } } return(fun) } diff --git a/man/type_abline.Rd b/man/type_abline.Rd index c815e16a2..3396d64fc 100644 --- a/man/type_abline.Rd +++ b/man/type_abline.Rd @@ -30,6 +30,11 @@ These functions add straight line(s) through the current plot. While \code{type_abline}, \code{type_hline}, and \code{type_vline} can be called in a base plot layer, we expect that they will typically be called as subsequent layers via \code{\link{tinyplot_add}}. + +Line parameters always refer to the original (pre-flip) variables. So, if +\code{flip = TRUE}, then \code{type_hline()} draws vertical line(s) at the given \code{y} +value(s), \code{type_vline()} draws horizontal line(s) at the given \code{x} value(s), +and \code{type_abline()} draws the line \eqn{y = a + bx} with the axes swapped. } \section{Recycling logic}{ @@ -97,6 +102,16 @@ tinyplot_add(type = type_hline(with(mtcars, tapply(mpg, cyl, mean))), lty = 2) # Similar idea for vline tinyplot_add(type = type_vline(with(mtcars, tapply(hp, cyl, mean))), lty = 2) +# +## Flipped plots + +# Line parameters refer to the original x and y variables, so "hline" is +# drawn vertically and "vline" horizontally when flip = TRUE +tinyplot(mpg ~ wt, data = mtcars, flip = TRUE) +tinyplot_add(type = type_hline(20), col = "hotpink") +tinyplot_add(type = type_vline(3), col = "dodgerblue") +tinyplot_add(type = type_abline(a = 37, b = -5), lty = 2) + # ## Recycling logic From f26acc90635a391683024e68899f1cd6ad5fd7a8 Mon Sep 17 00:00:00 2001 From: Grant McDermott Date: Wed, 16 Sep 2026 11:01:19 -0700 Subject: [PATCH 2/7] DRY --- R/type_abline.R | 127 +++++++++++++++++++++++++----------------------- R/type_hline.R | 62 +---------------------- R/type_vline.R | 62 +---------------------- 3 files changed, 67 insertions(+), 184 deletions(-) diff --git a/R/type_abline.R b/R/type_abline.R index ae7e8e2a4..0b9d7a10c 100644 --- a/R/type_abline.R +++ b/R/type_abline.R @@ -107,92 +107,95 @@ #' #' @export type_abline = function(a = 0, b = 1) { - data_abline = function(settings, ...) { + ablines_type(a = a, b = b, name = "abline") +} + + +# Shared internals for type_abline(), type_hline(), and type_vline() +ablines_type = function(a = NULL, b = NULL, h = NULL, v = NULL, name) { + params = list(a = a, b = b, h = h, v = v) + params = params[!vapply(params, is.null, logical(1))] + for (p in names(params)) assert_numeric(params[[p]], name = p) + data_ablines = function(settings, ...) { env2env(settings, environment(), c("datapoints", "lwd", "lty", "col")) if (nrow(datapoints) == 0) { - msg = "`type_abline() only works on existing plots with x and y data points." + msg = sprintf( + "`type_%s()` only works on existing plots with x and y data points.", + name + ) stop(msg, call. = FALSE) } # keep track of unique lty and lwd (needed for group catch / escape hatch - # later in draw_hline) - ul_lwd = length(unique(lwd)) - ul_lty = length(unique(lty)) - ul_col = length(unique(col)) - type_info = list(ul_lty = ul_lty, ul_lwd = ul_lwd, ul_col = ul_col) + # later in draw_ablines) + type_info = list( + ul_lty = length(unique(lty)), + ul_lwd = length(unique(lwd)), + ul_col = length(unique(col)) + ) env2env(environment(), settings, "type_info") } - draw_abline = function() { - fun = function(ifacet, iby, data_facet, icol, ilty, ilwd, - ngrps, nfacets, by_continuous, facet_by, - type_info, flip = FALSE, - ...) { - # flag for aesthetics by groups - grp_aes = type_info[["ul_col"]] == 1 || type_info[["ul_lty"]] == ngrps || type_info[["ul_lwd"]] == ngrps + draw_ablines = function(ifacet, iby, data_facet, icol, ilty, ilwd, + ngrps, nfacets, by_continuous, facet_by, + type_info, flip = FALSE, + ...) { + # flag for aesthetics by groups + grp_aes = type_info[["ul_col"]] == 1 || + type_info[["ul_lty"]] == ngrps || + type_info[["ul_lwd"]] == ngrps - if (length(a) != 1) { - if (!length(a) %in% c(ngrps, nfacets, ngrps * nfacets)) { - msg = "Length of 'a' must be 1, or equal to the number of facets or number of groups (or product thereof)." + # recycle each line parameter across groups and/or facets + for (p in names(params)) { + val = params[[p]] + if (length(val) != 1) { + if (!length(val) %in% c(ngrps, nfacets, ngrps * nfacets)) { + msg = sprintf( + "Length of '%s' must be 1, or equal to the number of facets or number of groups (or product thereof).", + p + ) stop(msg, call. = FALSE) } - if (!facet_by && length(a) == nfacets) { - a = a[ifacet] - if (!grp_aes && type_info[["ul_col"]] != ngrps) { - icol = 1 - } else if (by_continuous) { + if (!facet_by && length(val) == nfacets) { + val = val[ifacet] + if ((!grp_aes && type_info[["ul_col"]] != ngrps) || by_continuous) { icol = 1 } - } else if (!by_continuous && length(a) == ngrps * nfacets) { - a = a[ifacet * ngrps - c(ngrps - iby)] + } else if (!by_continuous && length(val) == ngrps * nfacets) { + val = val[ifacet * ngrps - c(ngrps - iby)] } else if (!by_continuous) { - a = a[iby] - } - } else if (!grp_aes) { - icol = 1 - } - - if (length(b) != 1) { - if (!length(b) %in% c(ngrps, nfacets, ngrps * nfacets)) { - msg = "Length of 'b' must be 1, or equal to the number of facets or number of groups (or product thereof)." - stop(msg, call. = FALSE) - } - if (!facet_by && length(b) == nfacets) { - b = b[ifacet] - if (!grp_aes && type_info[["ul_col"]] != ngrps) { - icol = 1 - } else if (by_continuous) { - icol = 1 - } - } else if (!by_continuous && length(b) == ngrps * nfacets) { - b = b[ifacet * ngrps - c(ngrps - iby)] - } else if (!by_continuous) { - b = b[iby] + val = val[iby] } } else if (!grp_aes) { icol = 1 } + params[[p]] = val + } - if (type_info[["ul_col"]] != 1 && !(type_info[["ul_lty"]] == ngrps || type_info[["ul_lwd"]] == ngrps)) { - icol = 1 - } + if (name == "abline" && type_info[["ul_col"]] != 1 && + !(type_info[["ul_lty"]] == ngrps || type_info[["ul_lwd"]] == ngrps)) { + icol = 1 + } - # under flip, y = a + b*x is drawn with the axes swapped, i.e. as - # x = (y - a) / b in plotting coordinates (vertical line if b == 0) - if (isTRUE(flip)) { - if (b == 0) { - abline(v = a, col = icol, lty = ilty, lwd = ilwd) + # Line parameters refer to the original (pre-flip) variables, so under flip + # we swap orientation: h <-> v, and y = a + b*x becomes x = (y - a) / b in + # plotting coordinates (i.e., a vertical line if b == 0) + if (isTRUE(flip)) { + params = switch(name, + hline = list(v = params[["h"]]), + vline = list(h = params[["v"]]), + abline = if (all(params[["b"]] == 0)) { + list(v = params[["a"]]) } else { - abline(a = -a / b, b = 1 / b, col = icol, lty = ilty, lwd = ilwd) + list(a = -params[["a"]] / params[["b"]], b = 1 / params[["b"]]) } - } else { - abline(a = a, b = b, col = icol, lty = ilty, lwd = ilwd) - } + ) } - return(fun) + + do.call(abline, c(params, list(col = icol, lty = ilty, lwd = ilwd))) } out = list( - draw = draw_abline(), - data = data_abline, - name = "abline" + draw = draw_ablines, + data = data_ablines, + name = name ) class(out) = "tinyplot_type" return(out) diff --git a/R/type_hline.R b/R/type_hline.R index 1c8d419a3..c7a0b8151 100644 --- a/R/type_hline.R +++ b/R/type_hline.R @@ -3,65 +3,5 @@ #' the number of groups or number of facets (or the product thereof). #' @export type_hline = function(h = 0) { - assert_numeric(h) - data_hline = function(settings, ...) { - env2env(settings, environment(), c("lwd", "lty", "col", "datapoints")) - - if (nrow(datapoints) == 0) { - msg = "`type_hline() only works on existing plots with x and y data points." - stop(msg, call. = FALSE) - } - # keep track of unique lty and lwd (needed for group catch / escape hatch - # later in draw_hline) - ul_lwd = length(unique(lwd)) - ul_lty = length(unique(lty)) - ul_col = length(unique(col)) - type_info = list(ul_lty = ul_lty, ul_lwd = ul_lwd, ul_col = ul_col) - env2env(environment(), settings, "type_info") - } - draw_hline = function() { - fun = function(ifacet, iby, data_facet, icol, ilty, ilwd, - ngrps, nfacets, by_continuous, facet_by, - type_info, flip = FALSE, - ...) { - # flag for aesthetics by groups - grp_aes = type_info[["ul_col"]] == 1 || type_info[["ul_lty"]] == ngrps || type_info[["ul_lwd"]] == ngrps - - if (length(h) != 1) { - if (!length(h) %in% c(ngrps, nfacets, ngrps * nfacets)) { - msg = "Length of 'h' must be 1, or equal to the number of facets or number of groups (or product thereof)." - stop(msg, call. = FALSE) - } - if (!facet_by && length(h) == nfacets) { - h = h[ifacet] - if (!grp_aes && type_info[["ul_col"]] != ngrps) { - icol = 1 - } else if (by_continuous) { - icol = 1 - } - } else if (!by_continuous && length(h) == ngrps * nfacets) { - h = h[ifacet * ngrps - c(ngrps - iby)] - } else if (!by_continuous) { - h = h[iby] - } - } else if (!grp_aes) { - icol = 1 - } - - # under flip, the y variable runs along the horizontal axis - if (isTRUE(flip)) { - abline(v = h, col = icol, lty = ilty, lwd = ilwd) - } else { - abline(h = h, col = icol, lty = ilty, lwd = ilwd) - } - } - return(fun) - } - out = list( - draw = draw_hline(), - data = data_hline, - name = "hline" - ) - class(out) = "tinyplot_type" - return(out) + ablines_type(h = h, name = "hline") } diff --git a/R/type_vline.R b/R/type_vline.R index a6b3d0331..146523c0a 100644 --- a/R/type_vline.R +++ b/R/type_vline.R @@ -3,65 +3,5 @@ #' @rdname type_abline #' @export type_vline = function(v = 0) { - assert_numeric(v) - data_vline = function(settings, ...) { - env2env(settings, environment(), c("datapoints", "lwd", "lty", "col")) - if (nrow(datapoints) == 0) { - msg = "`type_vline() only works on existing plots with x and y data points." - stop(msg, call. = FALSE) - } - # keep track of unique lty and lwd (needed for group catch / escape hatch - # later in draw_hline) - ul_lwd = length(unique(lwd)) - ul_lty = length(unique(lty)) - ul_col = length(unique(col)) - - type_info = list(ul_lty = ul_lty, ul_lwd = ul_lwd, ul_col = ul_col) - env2env(environment(), settings, "type_info") - } - draw_vline = function() { - fun = function(ifacet, iby, data_facet, icol, ilty, ilwd, - ngrps, nfacets, by_continuous, facet_by, - type_info, flip = FALSE, - ...) { - # flag for aesthetics by groups - grp_aes = type_info[["ul_col"]] == 1 || type_info[["ul_lty"]] == ngrps || type_info[["ul_lwd"]] == ngrps - - if (length(v) != 1) { - if (!length(v) %in% c(ngrps, nfacets, ngrps * nfacets)) { - msg = "Length of 'v' must be 1, or equal to the number of facets or number of groups (or product thereof)." - stop(msg, call. = FALSE) - } - if (!facet_by && length(v) == nfacets) { - v = v[ifacet] - if (!grp_aes && type_info[["ul_col"]] != ngrps) { - icol = 1 - } else if (by_continuous) { - icol = 1 - } - } else if (!by_continuous && length(v) == ngrps * nfacets) { - v = v[ifacet * ngrps - c(ngrps - iby)] - } else if (!by_continuous) { - v = v[iby] - } - } else if (!grp_aes) { - icol = 1 - } - - # under flip, the x variable runs along the vertical axis - if (isTRUE(flip)) { - abline(h = v, col = icol, lty = ilty, lwd = ilwd) - } else { - abline(v = v, col = icol, lty = ilty, lwd = ilwd) - } - } - return(fun) - } - out = list( - draw = draw_vline(), - data = data_vline, - name = "vline" - ) - class(out) = "tinyplot_type" - return(out) + ablines_type(v = v, name = "vline") } From dcebe2344d5799605db8c9f7822ed281ed10e94f Mon Sep 17 00:00:00 2001 From: Grant McDermott Date: Wed, 16 Sep 2026 11:01:51 -0700 Subject: [PATCH 3/7] knock-on effects --- R/type_errorbar.R | 2 +- man/type_errorbar.Rd | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/R/type_errorbar.R b/R/type_errorbar.R index c5e4ee954..146dbdd5b 100644 --- a/R/type_errorbar.R +++ b/R/type_errorbar.R @@ -62,7 +62,7 @@ #' tinytheme("classic") #' tinyplot(est ~ term, ymin = lwr, ymax = upr, data = coefs, type = "errorbar", #' flip = TRUE) -#' tinyplot_add(type = 'vline', lty = 2) +#' tinyplot_add(type = "hline", lty = 2) # "hline" b/c flip = TRUE (not vline!) #' #' tinytheme("basic") # back to basic theme for the remaining examples #' diff --git a/man/type_errorbar.Rd b/man/type_errorbar.Rd index b1e4bfe92..a6395a754 100644 --- a/man/type_errorbar.Rd +++ b/man/type_errorbar.Rd @@ -103,7 +103,7 @@ tinyplot(est ~ term, ymin = lwr, ymax = upr, data = coefs, tinytheme("classic") tinyplot(est ~ term, ymin = lwr, ymax = upr, data = coefs, type = "errorbar", flip = TRUE) -tinyplot_add(type = 'vline', lty = 2) +tinyplot_add(type = "hline", lty = 2) # "hline" b/c flip = TRUE (not vline!) tinytheme("basic") # back to basic theme for the remaining examples From f053730ea8c7941c018cbacf0a3d1c8a6d5b891e Mon Sep 17 00:00:00 2001 From: Grant McDermott Date: Wed, 16 Sep 2026 11:02:09 -0700 Subject: [PATCH 4/7] more knock on --- vignettes/gallery_figs/likert.R | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vignettes/gallery_figs/likert.R b/vignettes/gallery_figs/likert.R index 1348b20b1..05c696065 100644 --- a/vignettes/gallery_figs/likert.R +++ b/vignettes/gallery_figs/likert.R @@ -25,5 +25,5 @@ plt( theme = list("clean2", palette.qualitative = pal), main = "Likert example with \"Unsure\" category offset" ) -plt_add(type = "vline") -# plt_add(type = "vline", v = 1, lty = 2) ## optional +plt_add(type = "hline") # note: "hline" b/c flip = TRUE (not "vline"!) +# plt_add(type = type_hline(1), lty = 2) ## optional \ No newline at end of file From 773adcd76dd7c5ac794500076cf6d6e73475c77f Mon Sep 17 00:00:00 2001 From: Grant McDermott Date: Wed, 16 Sep 2026 11:02:33 -0700 Subject: [PATCH 5/7] test --- inst/tinytest/_tinysnapshot/flip_ablines.svg | 97 ++++++++++++++++++++ inst/tinytest/test-flip.R | 11 +++ inst/tinytest/test-type_pointrange.R | 2 +- 3 files changed, 109 insertions(+), 1 deletion(-) create mode 100644 inst/tinytest/_tinysnapshot/flip_ablines.svg diff --git a/inst/tinytest/_tinysnapshot/flip_ablines.svg b/inst/tinytest/_tinysnapshot/flip_ablines.svg new file mode 100644 index 000000000..c23b230bd --- /dev/null +++ b/inst/tinytest/_tinysnapshot/flip_ablines.svg @@ -0,0 +1,97 @@ + + + + + + + + + + + + + +Flipped h/v/ablines +mpg +wt + + + + + + +10 +15 +20 +25 +30 + + + + + +2 +3 +4 +5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inst/tinytest/test-flip.R b/inst/tinytest/test-flip.R index c2e9537dd..0ae0bf36f 100644 --- a/inst/tinytest/test-flip.R +++ b/inst/tinytest/test-flip.R @@ -140,3 +140,14 @@ f = function() { ) } expect_snapshot_plot(f, label = "flip_type_h_grouped") + + +# flipped straight line types (#733) + +f = function() { + tinyplot(mpg ~ wt, data = mtcars, flip = TRUE, main = "Flipped h/v/ablines") + tinyplot_add(type = type_hline(20), col = "hotpink") + tinyplot_add(type = type_vline(3), col = "dodgerblue") + tinyplot_add(type = type_abline(a = 37, b = -5), lty = 2) +} +expect_snapshot_plot(f, label = "flip_ablines") diff --git a/inst/tinytest/test-type_pointrange.R b/inst/tinytest/test-type_pointrange.R index a0e4a2cee..b56af741d 100644 --- a/inst/tinytest/test-type_pointrange.R +++ b/inst/tinytest/test-type_pointrange.R @@ -68,7 +68,7 @@ fun = function() { flip = TRUE ) tinyplot_add(type = "ribbon") - tinyplot_add(type = "vline", lty = 2) + tinyplot_add(type = "hline", lty = 2) # "hline" b/c flip = TRUE (not vline!) } expect_snapshot_plot(fun, label = "pointrange_with_layers_flipped") From 7ab054caac0689b850706a5eacb7334bc1a88486 Mon Sep 17 00:00:00 2001 From: Grant McDermott Date: Wed, 16 Sep 2026 11:03:44 -0700 Subject: [PATCH 6/7] news --- NEWS.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/NEWS.md b/NEWS.md index 7ddc2d141..17be8be0a 100644 --- a/NEWS.md +++ b/NEWS.md @@ -229,6 +229,11 @@ related to plot layering. See "Bug fixes" below. - `type_text()` no longer converts a categorical axis to a numeric one. (#730 @grantmcdermott) +- `type_hline()`, `type_vline()`, and `type_abline()` now respect + `flip = TRUE`, so that (e.g.) `h` refers to the flipped `y` variable and is + drawn vertically. Code that previously used `type_vline()` as a workaround + for a vertical line on a flipped plot should switch to `type_hline()`, and + vice versa. (#733 @grantmcdermott) - The `adjust` argument of `type_density()`, `type_violin()`, and `type_ridge()` was accepted but never passed on to the underlying `density()` call, so it silently did nothing. (#734 @grantmcdermott) From 284e5a7f95ad03560ecb28b0510db5ca330eaa34 Mon Sep 17 00:00:00 2001 From: Grant McDermott Date: Wed, 16 Sep 2026 11:38:15 -0700 Subject: [PATCH 7/7] copilot catches --- R/type_abline.R | 12 +- R/type_barplot.R | 6 +- inst/tinytest/_tinysnapshot/abline.svg | 95 ++++++++++ .../flip_ablines_facet_zero_slope.svg | 165 ++++++++++++++++++ inst/tinytest/test-flip.R | 7 + inst/tinytest/test-type_abline.R | 7 + man/type_barplot.Rd | 6 +- 7 files changed, 288 insertions(+), 10 deletions(-) create mode 100644 inst/tinytest/_tinysnapshot/abline.svg create mode 100644 inst/tinytest/_tinysnapshot/flip_ablines_facet_zero_slope.svg diff --git a/R/type_abline.R b/R/type_abline.R index 0b9d7a10c..614598a31 100644 --- a/R/type_abline.R +++ b/R/type_abline.R @@ -113,8 +113,11 @@ type_abline = function(a = 0, b = 1) { # Shared internals for type_abline(), type_hline(), and type_vline() ablines_type = function(a = NULL, b = NULL, h = NULL, v = NULL, name) { - params = list(a = a, b = b, h = h, v = v) - params = params[!vapply(params, is.null, logical(1))] + params = switch(name, + abline = list(a = a, b = b), + hline = list(h = h), + vline = list(v = v) + ) for (p in names(params)) assert_numeric(params[[p]], name = p) data_ablines = function(settings, ...) { env2env(settings, environment(), c("datapoints", "lwd", "lty", "col")) @@ -177,12 +180,13 @@ ablines_type = function(a = NULL, b = NULL, h = NULL, v = NULL, name) { # Line parameters refer to the original (pre-flip) variables, so under flip # we swap orientation: h <-> v, and y = a + b*x becomes x = (y - a) / b in - # plotting coordinates (i.e., a vertical line if b == 0) + # plotting coordinates (i.e., a vertical line if b == 0). Note that abline() + # only uses the first a/b values, so we only check the first slope. if (isTRUE(flip)) { params = switch(name, hline = list(v = params[["h"]]), vline = list(h = params[["v"]]), - abline = if (all(params[["b"]] == 0)) { + abline = if (params[["b"]][1] == 0) { list(v = params[["a"]]) } else { list(a = -params[["a"]] / params[["b"]], b = 1 / params[["b"]]) diff --git a/R/type_barplot.R b/R/type_barplot.R index 899aa9140..10fea377e 100644 --- a/R/type_barplot.R +++ b/R/type_barplot.R @@ -213,7 +213,7 @@ #' flip = TRUE, yaxl = "percent", #' theme = list("clean2", palette.qualitative = hcols) #' ) -#' tinyplot_add(type = "vline", col = "white") +#' tinyplot_add(type = "hline", col = "white") # "hline" b/c flip = TRUE #' #' # #' ## Offset examples @@ -255,8 +255,8 @@ #' theme = list("clean2", palette.qualitative = pal), #' main = "Hypothetical Likert example with category offset" #' ) -#' tinyplot_add(type = "vline") -#' tinyplot_add(type = "vline", v = 1, lty = 2) +#' tinyplot_add(type = "hline") # "hline" b/c flip = TRUE +#' tinyplot_add(type = type_hline(1), lty = 2) # ditto #' #' # #' ## Implicit zeros and empty cells (see the section of the same name above) diff --git a/inst/tinytest/_tinysnapshot/abline.svg b/inst/tinytest/_tinysnapshot/abline.svg new file mode 100644 index 000000000..bb2b01a36 --- /dev/null +++ b/inst/tinytest/_tinysnapshot/abline.svg @@ -0,0 +1,95 @@ + + + + + + + + + + + + + +wt +mpg + + + + + +2 +3 +4 +5 + + + + + + +10 +15 +20 +25 +30 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inst/tinytest/_tinysnapshot/flip_ablines_facet_zero_slope.svg b/inst/tinytest/_tinysnapshot/flip_ablines_facet_zero_slope.svg new file mode 100644 index 000000000..30f4adcc3 --- /dev/null +++ b/inst/tinytest/_tinysnapshot/flip_ablines_facet_zero_slope.svg @@ -0,0 +1,165 @@ + + + + + + + + + + + + + +mpg +wt + + + + + + + + + + + + + + + +10 +15 +20 +25 +30 + + + + + +2 +3 +4 +5 + +0 + + + + + + + + + + + + + + + + +10 +15 +20 +25 +30 + + + + + +2 +3 +4 +5 + +1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inst/tinytest/test-flip.R b/inst/tinytest/test-flip.R index 0ae0bf36f..3280717a3 100644 --- a/inst/tinytest/test-flip.R +++ b/inst/tinytest/test-flip.R @@ -151,3 +151,10 @@ f = function() { tinyplot_add(type = type_abline(a = 37, b = -5), lty = 2) } expect_snapshot_plot(f, label = "flip_ablines") + +f = function() { + tinyplot(mpg ~ wt, facet = ~am, data = mtcars, flip = TRUE) + tinyplot_add(type = type_hline(c(15, 25)), col = "hotpink") + tinyplot_add(type = type_abline(a = 20, b = 0), lty = 2) +} +expect_snapshot_plot(f, label = "flip_ablines_facet_zero_slope") diff --git a/inst/tinytest/test-type_abline.R b/inst/tinytest/test-type_abline.R index 31e81a7d9..4072ae1d5 100644 --- a/inst/tinytest/test-type_abline.R +++ b/inst/tinytest/test-type_abline.R @@ -56,6 +56,13 @@ f = function() { } expect_snapshot_plot(f, label = "vline_univariate_y1") +f = function() { + tinyplot(mpg ~ wt, data = mtcars) + tinyplot_add(type = type_abline(a = 37, b = -5), lty = 2) + tinyplot_add(type = type_abline(a = 20, b = 0), col = "hotpink") +} +expect_snapshot_plot(f, label = "abline") + ## TODO: uncomment this when ready to test. Probably after the tinyplot_add ## refactor to save in an environment instead of global option # f = function() { diff --git a/man/type_barplot.Rd b/man/type_barplot.Rd index 58ac257a7..eebed4ee8 100644 --- a/man/type_barplot.Rd +++ b/man/type_barplot.Rd @@ -246,7 +246,7 @@ tinyplot( flip = TRUE, yaxl = "percent", theme = list("clean2", palette.qualitative = hcols) ) -tinyplot_add(type = "vline", col = "white") +tinyplot_add(type = "hline", col = "white") # "hline" b/c flip = TRUE # ## Offset examples @@ -288,8 +288,8 @@ tinyplot( theme = list("clean2", palette.qualitative = pal), main = "Hypothetical Likert example with category offset" ) -tinyplot_add(type = "vline") -tinyplot_add(type = "vline", v = 1, lty = 2) +tinyplot_add(type = "hline") # "hline" b/c flip = TRUE +tinyplot_add(type = type_hline(1), lty = 2) # ditto # ## Implicit zeros and empty cells (see the section of the same name above)