diff --git a/NEWS.md b/NEWS.md index 4c28ea5d..bca7b8b4 100644 --- a/NEWS.md +++ b/NEWS.md @@ -46,7 +46,7 @@ margin spacing and related plot elements to reduce whitespace and improve the overall plot aesthetic. The `?tinytheme` help file and online [Themes](https://grantmcdermott.com/tinyplot/vignettes/themes.html) vignette cover the new features in detail. But see below for the highlights. -(#549, #591, #595, #606 @grantmcdermott, @vincentarelbundock) +(#549, #591, #595, #606, #614 @grantmcdermott, @vincentarelbundock, @zeileis) New theme features: @@ -80,6 +80,39 @@ New theme features: `tinytheme()` or `tinyplot(..., theme = )`. Companion functions `tinytheme_list()` and `tinytheme_unregister()` further support this functionality. (#608 @grantmcdermott) +- We adopt a new logic for selecting the default colour for single-group + (i.e. plots without a `by` grouping) versus multi-group displays. The upshot + is that, for many themes, single-group displays will simply default to + "black", whereas multi-group displays will drop "black" and only present + colour palettes. This behaviour is operationalized through the new + `col.default` parameter, settable via `tpar()` or within a theme. + (#614 @grantmcdermott @zeileis) + - Themes whose palette already leads with a non-black colour (e.g. + `"clean(2)"`, `"dark"`, `"nber"`, `"web"`) consistently use that colour for + single-group displays too. + - The ggplot2-inspired themes (`"bw"`, `"classic"`, `"linedraw"`, and + `"minimal"`) continue to use black for single-group display. But they now + drop the leading black from their _grouped_ palettes. (Note that these + themes also use the default `"ggplot2"` palette now, so grouped plots start + at the familiar salmon hue.) + - Similarly, the `"ipsum2"` and `"broadsheet"` themes use the + Okabe-Ito palette less its leading black, so grouped plots start at orange. + The `"float"` and `"void"` themes also default to black for single-group + displays. +- Relatedly, the _fill_ of single-group `"boxplot"`, `"violin"`, `"barplot"`, + and `"histogram"` displays is now unified across these four types, so that a + single-group plot looks the same regardless of which one you reach for. + (#614 @grantmcdermott @zeileis) + - When the resolved default colour is _chromatic_ (e.g. the blue of `"clean"` + or the teal of `"dark"`), the fill is a lighter-but-opaque tint of it; + following the same sequential HCL ramp that `"ridge"` type plots have been + using for a while. + - When the default is _achromatic_ (black, as in the plain default and the + `"bw"`/`"classic"`/`"ipsum"` themes), all four types share a neutral + `"lightgray"` fill, matching base R's `hist()` and `boxplot()` defaults. + Note that this does imply a change for `"barplot"` types, which previously + used a slightly darker `"grey"` (to match base R's `barplot()`), but we + decided internal consistency was the more important feature to prioritize. Theme fixes: diff --git a/R/by_aesthetics.R b/R/by_aesthetics.R index b53b8035..2595b63f 100755 --- a/R/by_aesthetics.R +++ b/R/by_aesthetics.R @@ -122,6 +122,44 @@ match_palette_name = function(name, candidates) { charmatch(normalize(name), normalize(candidates)) } +## Resolve a palette spec to its full concrete colour vector (or NULL if it +## can't be resolved to a discrete set, e.g. a continuous hcl palette). Used to +## support relative `col.default` indices. +resolve_palette_to_colors = function(palette) { + if (is.null(palette)) return(NULL) + if (is.character(palette) && length(palette) > 1) return(unname(palette)) + if (is.character(palette) && length(palette) == 1) { + discrete_pals = palette.pals() + idx = match_palette_name(palette, discrete_pals) + if (!is.na(idx) && idx >= 1L) { + return(unname(palette.colors(palette = discrete_pals[idx]))) + } + } + NULL +} + +## Resolve a (possibly relative) `col.default` against the qualitative palette. +## `col_default` may be NULL, a character colour, or a numeric index into the +## palette. A negative index additionally *drops* that colour from the palette +## returned for grouped displays. So `col.default = -1` with an "Okabe-Ito" +## palette uses black (the leading colour) for single-group plots and an +## Okabe-Ito-minus-black palette for grouped plots, avoiding the need to keep a +## separate "no-black" palette copy around (#598, #614). Returns a list with the +## resolved single-group `col_default` and the (possibly trimmed) `palette`. +resolve_col_default = function(col_default, palette_spec) { + if (!is.numeric(col_default)) { + return(list(col_default = col_default, palette = palette_spec)) + } + full = resolve_palette_to_colors(palette_spec) + if (is.null(full)) { + return(list(col_default = NULL, palette = palette_spec)) + } + i = as.integer(col_default) + out_col = full[abs(i)] + if (i < 0L) palette_spec = full[-abs(i)] + list(col_default = out_col, palette = palette_spec) +} + ## Handle direct color input via `col` arg. Returns colors or NULL if not applicable. resolve_manual_colors = function(col, ngrps, gradient, ordered, alpha, adjustcolor) { if (is.null(col) || !is.atomic(col) || !is.vector(col)) { @@ -296,16 +334,32 @@ by_col = function(col, palette, alpha, by_ordered, by_continuous, ngrps, adjustc if (is_by_keyword(col)) col = NULL + pal_theme = if (ordered || gradient) { + get_tpar("palette.sequential", default = NULL) + } else { + get_tpar("palette.qualitative", default = NULL) + } + + # Resolve a (possibly relative) col.default against the qualitative palette. + # A relative (negative) index also drops the chosen colour from the grouped + # palette, so e.g. `col.default = -1` uses the palette's leading colour for + # single-group displays and the palette-minus-that-colour for grouped ones + # (see resolve_col_default). col.default only applies to qualitative displays. + if (!ordered && !gradient) { + cd = resolve_col_default(get_tpar("col.default", default = NULL), pal_theme) + pal_theme = cd[["palette"]] + # Single-group default colour (theme-settable via tpar). When NULL, defer to + # the usual palette resolution below (first qualitative colour, or palette()[1]). + if (is.null(col) && ngrps == 1L) { + col = cd[["col_default"]] + } + } + cols = resolve_manual_colors(col, ngrps, gradient, ordered, alpha, adjustcolor) if (!is.null(cols)) { return(cols) } - pal_theme = if (ordered || gradient) { - get_tpar("palette.sequential", default = NULL) - } else { - get_tpar("palette.qualitative", default = NULL) - } cols = resolve_palette_colors( palette = palette, theme_palette = pal_theme, @@ -330,10 +384,15 @@ by_bg = function(bg, fill, col, palette, alpha, by_ordered, by_continuous, ngrps ordered = if (is.null(by_ordered)) FALSE else by_ordered gradient = if (is.null(by_continuous)) FALSE else by_continuous pal_theme = if (ordered || gradient) { - get_tpar("palette.sequential", default = NULL) + get_tpar("palette.sequential", default = NULL) } else { get_tpar("palette.qualitative", default = NULL) } + # Mirror by_col(): a relative col.default trims the grouped palette, so + # grouped fills stay in step with grouped line colours. + if (!ordered && !gradient) { + pal_theme = resolve_col_default(get_tpar("col.default", default = NULL), pal_theme)[["palette"]] + } bg = resolve_palette_colors( palette = palette, theme_palette = pal_theme, @@ -352,6 +411,26 @@ by_bg = function(bg, fill, col, palette, alpha, by_ordered, by_continuous, ngrps } else if (!is.null(col)) { bg = adjustcolor(col, ribbon.alpha) } + } else if (ngrps == 1L && is.null(bg) && type %in% c("boxplot", "violin", "barplot", "histogram")) { + # Single-group fill tracks the theme's *default* colour (col.default -> + # palette[1] -> black) so that themed box/violin/bar/histogram plots match + # their own multi-group counterparts. We resolve this default independently + # of `col` so that a user-supplied outline colour (e.g. `col = "white"`) + # doesn't bleed into the fill. + fill_base = by_col( + col = NULL, palette = palette, alpha = 1, by_ordered = by_ordered, + by_continuous = by_continuous, ngrps = 1L, adjustcolor = adjustcolor + ) + # For a *chromatic* default the fill is a lighter-but-opaque tint of that + # colour (via seq_palette, the same HCL ramp used for ridge fills and legend + # swatches), so it reads cleanly over grid lines unlike alpha blending. For + # an *achromatic* default (typically black, e.g. the "bw"/"classic"/"ipsum" + # themes and the plain default) seq_palette's light endpoint can't reach the + # neutral "lightgray" used by the no-theme path and base R's hist()/boxplot() + # -- so we use that literal directly, keeping all black-default single-group + # fills consistent regardless of whether a theme palette happens to be set. + achromatic = is_achromatic(fill_base) + bg = if (achromatic) "lightgray" else seq_palette(fill_base, n = 3)[3] } bg diff --git a/R/tinytheme.R b/R/tinytheme.R index 98239403..556a8230 100644 --- a/R/tinytheme.R +++ b/R/tinytheme.R @@ -418,11 +418,12 @@ theme_classic = modifyList(theme_dynamic, list( bty = "l", cex.axis = 0.8, cex.cap = 0.8, + col.default = -1L, # black single-group; drop it from the grouped palette facet.bg = NULL, font.main = 1, gap.axis = 0.1, gap.lab = 0.4, - palette.qualitative = "Okabe-Ito" + palette.qualitative = "ggplot2" )) # derivatives of "clean" @@ -439,6 +440,7 @@ theme_clean2 = modifyList(theme_clean, list( theme_bw = modifyList(theme_clean, list( tinytheme = "bw", cex.axis = 0.8, + col.default = -1L, # black single-group; drop it from the grouped palette font.main = 1, gap.axis = 0.1, gap.lab = 0.4, @@ -447,7 +449,7 @@ theme_bw = modifyList(theme_clean, list( grid.lwd = 0.5, lwd = 0.5, lwd.axis = 0.5, - palette.qualitative = "Okabe-Ito" + palette.qualitative = "ggplot2" )) theme_linedraw = modifyList(theme_bw, list( @@ -477,6 +479,7 @@ theme_ipsum = modifyList(theme_minimal, list( adj.xlab = 1, adj.ylab = 1, cex.lab = 0.8, + col.default = "black", # bespoke palette doesn't lead with black; pin it font.main = 2, font.sub = 1, font.cap = 3, @@ -493,12 +496,15 @@ theme_ipsum2 = modifyList(theme_minimal, list( bty = "n", font.sub = 3, adj.ylab = 1, - adj.xlab = 1 + adj.xlab = 1, + col.default = -1L, # black single-group; drop it from the grouped palette + palette.qualitative = "Okabe-Ito" # keep Okabe-Ito, not the ggplot2 palette )) theme_dark = modifyList(theme_minimal, list( tinytheme = "dark", bg = "#1A1A1A", + col.default = NULL, # no fixed default: single-group uses palette[1] (Set 2) fg = "#BBBBBB", # col = "white", col.xaxs = "#BBBBBB", @@ -519,11 +525,13 @@ theme_dark = modifyList(theme_minimal, list( theme_ridge = modifyList(theme_clean, list( tinytheme = "ridge", + col.default = "black", # keep black ridgelines; Zissou is for gradient fills palette.qualitative = "Zissou 1", grid = FALSE )) theme_ridge2 = modifyList(theme_clean2, list( tinytheme = "ridge2", + col.default = "black", # keep black ridgelines; Zissou is for gradient fills palette.qualitative = "Zissou 1", grid = FALSE )) @@ -539,6 +547,7 @@ theme_socviz = modifyList(theme_minimal, list( cex.lab = 1, cex.main = 1.4, cex.sub = 1.05, + col.default = "black", # bespoke palette doesn't lead with black; pin it col.xaxs = "gray10", col.yaxs = "gray10", facet.bg = NULL, @@ -565,6 +574,7 @@ theme_broadsheet = modifyList(theme_dynamic, list( bty = "n", cex.cap = 0.8, col.cap = "gray40", + col.default = -1L, # black single-group; drop it from the grouped palette col.sub = "gray40", font.main = 2, gap.axis = 0.1, @@ -584,6 +594,7 @@ theme_broadsheet = modifyList(theme_dynamic, list( theme_nber = modifyList(theme_broadsheet, list( tinytheme = "nber", bg = "#F2F7FB", + col.default = NULL, # override broadsheet's black: nber palette leads with blue cex.cap = 1, cex.main = 1.4, cex.sub = 1, @@ -641,6 +652,7 @@ theme_tufte = modifyList(theme_dynamic, list( theme_float = modifyList(theme_tufte, list( tinytheme = "float", + col.default = "black", gap.axis = 0, gap.lab = 0.7, lab = c(5, 5, 7), @@ -650,6 +662,7 @@ theme_float = modifyList(theme_tufte, list( theme_void = modifyList(theme_dynamic, list( tinytheme = "void", + col.default = "black", facet.bg = NULL, facet.border = NA, font.main = 1, diff --git a/R/tpar.R b/R/tpar.R index 3b437c1c..23d89de7 100644 --- a/R/tpar.R +++ b/R/tpar.R @@ -55,6 +55,7 @@ #' * `cairo`: Logical indicating whether \code{\link[grDevices]{cairo_pdf}} should be used when writing plots to PDF. If `FALSE`, then \code{\link[grDevices]{pdf}} will be used instead, with implications for embedding (non-standard) fonts. Only used if `tinyplot(..., file = ".pdf")` is called. Defaults to the value of `capabilities("cairo")`. #' * `cex.cap`: Numeric expansion factor for the plot caption text. Defaults to `1` for the default, basic, and dynamic themes, and `0.8` for clean/classic and their descendants. #' * `col.cap`: Character specifying the colour of the plot caption. Defaults to `"black"`. +#' * `col.default`: Default colour for single-group displays (i.e. plots without a `by` grouping). Can be `NULL`, a length-1 character colour, or a length-1 numeric index into `palette.qualitative`. Defaults to `NULL`, in which case the first colour of the active qualitative palette is used (or base `palette()[1]`, typically black, if no theme is set). A character value sets the single-group colour independently of the multi-group palette. A numeric value `i` selects the `i`th colour of `palette.qualitative` as the single-group default; a *negative* index additionally drops that colour from the palette used for grouped plots. For example, `col.default = -1` paired with `palette.qualitative = "Okabe-Ito"` uses black (the leading palette colour) for single-group plots and an Okabe-Ito-minus-black palette for grouped plots, avoiding the need to maintain a separate "no-black" palette. #' * `font.cap`: Integer specifying the font face for the plot caption (`1` = plain, `2` = bold, `3` = italic, `4` = bold italic). Defaults to `1`. #' * `line.cap`: Numeric specifying the margin line on which to draw the caption. If `NULL` (default), computed automatically based on the available bottom margin. #' * `dynmar`: Logical indicating whether `tinyplot` should attempt dynamic adjustment of margins to reduce whitespace and/or account for spacing of text elements (e.g., long horizontal y-axis labels). Note that this parameter is tightly coupled to internal `tinythemes()` logic and should _not_ be adjusted manually unless you really know what you are doing or don't mind risking unintended consequences to your plot. @@ -234,6 +235,7 @@ known_tpar = c( "cex.xlab", "cex.ylab", "col.cap", + "col.default", "col.xaxs", "col.yaxs", "cairo", @@ -305,6 +307,14 @@ assert_tpar = function(.tpar) { assert_string(.tpar[["grid.bg"]], null.ok = TRUE, name = "grid.bg") assert_numeric(.tpar[["fmar"]], len = 4, null.ok = TRUE, name = "fmar") + col.default = .tpar[["col.default"]] + if (!is.null(col.default)) { + if (!is.character(col.default) && !is.numeric(col.default)) { + stop("col.default needs to be NULL, a (length-1) character colour, or a (length-1) numeric palette index", call. = FALSE) + } + assert_true(length(col.default) == 1, name = "length(col.default)==1") + } + facet.col = .tpar[["facet.col"]] if (!is.null(facet.col)) { if (!is.null(facet.col) && !is.numeric(facet.col) && !is.character(facet.col)) { @@ -367,6 +377,10 @@ init_tpar = function(rm_hook = FALSE) { .tpar$grid.lty = if (is.null(getOption("tinyplot_grid.lty"))) "dotted" else getOption("tinyplot_grid.lty") .tpar$grid.lwd = if (is.null(getOption("tinyplot_grid.lwd"))) 1 else as.numeric(getOption("tinyplot_grid.lwd")) + # Default colour for single-group displays (NULL defers to the first + # qualitative palette colour, or base palette()[1] if no theme is active) + .tpar$col.default = if (is.null(getOption("tinyplot_col.default"))) NULL else getOption("tinyplot_col.default") + # Legend justification .tpar$ljust = if (is.null(getOption("tinyplot_ljust"))) "left" else getOption("tinyplot_ljust") diff --git a/R/type_barplot.R b/R/type_barplot.R index 91322502..c5f99e84 100644 --- a/R/type_barplot.R +++ b/R/type_barplot.R @@ -280,8 +280,11 @@ data_barplot = function(width = 5/6, beside = FALSE, center = FALSE, offset = NU ## default color palette ngrps = length(unique(datapoints$by)) if (ngrps == 1L && null_palette) { - if (is.null(col)) col = par("fg") - if (is.null(bg)) bg = "grey" + # With a theme palette active, leave bg = NULL so the fill tracks + # the resolved border colour (see by_bg). Otherwise use the neutral + # "lightgray" shared by all single-group area fills (matches base R + # hist()/boxplot()). + if (is.null(bg) && is.null(get_tpar("palette.qualitative", default = NULL))) bg = "lightgray" } else { if (is.null(bg)) bg = "by" } diff --git a/R/type_boxplot.R b/R/type_boxplot.R index 6610846c..51504fe6 100644 --- a/R/type_boxplot.R +++ b/R/type_boxplot.R @@ -113,8 +113,9 @@ data_boxplot = function(boxwex = 0.8) { # Check if user provided palette before substitute) if (length(unique(datapoints[["by"]])) == 1 && null_palette) { - if (is.null(col)) col = par("fg") - if (is.null(bg)) bg = "lightgray" + # With a theme palette active, leave bg = NULL so the fill tracks + # the resolved border colour (see by_bg). Otherwise use neutral grey. + if (is.null(bg) && is.null(get_tpar("palette.qualitative", default = NULL))) bg = "lightgray" } else { if (is.null(bg)) bg = "by" } diff --git a/R/type_histogram.R b/R/type_histogram.R index 5d504d22..4f461b7e 100644 --- a/R/type_histogram.R +++ b/R/type_histogram.R @@ -101,15 +101,20 @@ data_histogram = function(breaks = "Sturges", hright = right fun = function(settings, .breaks = hbreaks, .freebreaks = hfree.breaks, .freq = hfreq, .right = hright, .drop.zeros = hdrop.zeros, ...) { - env2env(settings, environment(), c("palette", "bg", "col", "plot", "datapoints", "ymin", "ymax", "xmin", "xmax", "freq", "ylab", "xlab", "facet", "ribbon.alpha")) + env2env(settings, environment(), c("palette", "bg", "col", "plot", "datapoints", "ymin", "ymax", "xmin", "xmax", "freq", "ylab", "xlab", "facet", "ribbon.alpha", "by", "null_by", "null_palette")) hbreaks = ifelse(!sapply(.breaks, is.null), .breaks, "Sturges") - if (is.null(by) && is.null(palette)) { - if (is.null(col)) col = par("fg") - if (is.null(bg)) bg = "lightgray" - } else { - if (is.null(bg)) bg = ribbon.alpha + # Multi-group displays fill from the palette at `ribbon.alpha` + # transparency via the `ribbon.alpha` keyword. For single-group displays + # with a theme palette active we leave `bg = NULL` so the fill tracks the + # resolved border colour (see by_bg), which honours `col.default`. With + # no theme palette, single-group uses the neutral "lightgray" shared by + # all single-group area fills (matches base R hist()). + if (is.null(bg) && !null_by) { + bg = ribbon.alpha + } else if (is.null(bg) && null_by && null_palette && is.null(get_tpar("palette.qualitative", default = NULL))) { + bg = "lightgray" } if (!.freebreaks) xbreaks = hist(datapoints$x, breaks = hbreaks, right = .right, plot = FALSE)$breaks diff --git a/R/type_ridge.R b/R/type_ridge.R index cd79cc03..67f534af 100644 --- a/R/type_ridge.R +++ b/R/type_ridge.R @@ -64,7 +64,12 @@ #' of the ridge densities. Note that a singular value is expected; if multiple #' colors are provided then only the first will be used. This argument is mostly #' useful for the aesthetic effect of drawing a common outline color in -#' combination with gradient fills. See Examples. +#' combination with gradient fills. See Examples. If left unset here, the +#' outline color can also be supplied via the top-level `tinyplot(..., col =)` +#' call (the `type_ridge(col =)` argument takes precedence if both are given). +#' When neither is supplied, the outline defaults to the active theme's +#' `col.default` (see [tpar]), falling back to the first color of the theme's +#' qualitative palette, or black if no theme is set. #' @param alpha Numeric in the range `[0,1]` for adjusting the alpha #' transparency of the density fills. In most cases, will default to a value of #' 1, i.e. fully opaque. But for some `by` grouped plots (excepting the special @@ -257,6 +262,12 @@ data_ridge = function(bw = "nrd0", adjust = 1, kernel = "gaussian", n = 512, fun = function(settings, ...) { env2env(settings, environment(), c("datapoints", "yaxt", "xaxt", "null_by")) + # `col` may arrive either via the top-level `tinyplot(..., col =)` call + # (stored in settings) or via the `type_ridge(col =)` constructor arg. The + # constructor arg takes precedence; otherwise fall back to the settings + # value so that a user-supplied `col` is respected. (#598) + if (is.null(col)) col = settings[["col"]] + # catch for special cases anyby = !null_by x_by = anyby && identical(datapoints$x, datapoints$by) @@ -396,7 +407,24 @@ data_ridge = function(bw = "nrd0", adjust = 1, kernel = "gaussian", n = 512, breaks[length(breaks)] = pmax(breaks[length(breaks)], xlim[2L]) } - if (is.null(col) && (!anyby || x_by)) col = "black" + # Single-group (or x_by) ridges: default the outline colour consistently + # with the other plot types. An explicit `col.default` wins; otherwise fall + # back to the first colour of the active qualitative palette (e.g. blue under + # "clean"), or base palette()[1] (black) when no theme palette is set. (#598) + if (is.null(col) && (!anyby || x_by)) { + col = get_tpar("col.default", default = NULL) + if (is.null(col)) { + pal_q = .tpar[["palette.qualitative"]] + col = if (!is.null(pal_q)) { + resolve_palette_spec( + pal_q, ngrps = 1L, gradient = FALSE, ordered = FALSE, + alpha = 1, adjustcolor = adjustcolor + ) + } else { + palette()[1L] + } + } + } # For ridge themes without groups, a numeric bg (e.g. bg = 0.2) should # produce transparent gray, not a transparent palette colour. (#547) @@ -458,10 +486,15 @@ draw_ridge = function() { if (is.null(ibg)) { pal_q = .tpar[["palette.qualitative"]] # For non-ridge themes with a palette, derive fill from the first palette - # colour. We need palette.colors() here because the palette is still just - # a string name at this point (not yet resolved to colours). (#547) + # colour. The theme palette may be a name (e.g. "Tableau 10") or a vector + # of colours (e.g. the ipsum/socviz themes), so resolve it to an actual + # colour rather than assuming a name. (#547, #598) default_bg = if (!ridge_theme && !is.null(pal_q)) { - seq_palette(palette.colors(1, palette = pal_q), n = 2)[2] + pal_q1 = resolve_palette_spec( + pal_q, ngrps = 1L, gradient = FALSE, ordered = FALSE, + alpha = 1, adjustcolor = adjustcolor + ) + seq_palette(pal_q1, n = 2)[2] } else { "gray" } diff --git a/R/type_spineplot.R b/R/type_spineplot.R index 36ea2d78..bca7aebf 100644 --- a/R/type_spineplot.R +++ b/R/type_spineplot.R @@ -79,7 +79,7 @@ type_spineplot = function(breaks = NULL, tol.ylab = 0.05, off = NULL, xlevels = #' @importFrom grDevices nclass.Sturges data_spineplot = function(off = NULL, breaks = NULL, xlevels = xlevels, ylevels = ylevels, xaxlabels = NULL, yaxlabels = NULL, weights = NULL) { fun = function(settings, ...) { - env2env(settings, environment(), c("datapoints", "xlim", "ylim", "facet", "facet.args", "by", "xaxb", "yaxb", "null_by", "null_facet", "null_palette", "col", "bg", "axes", "xaxt", "yaxt")) + env2env(settings, environment(), c("datapoints", "xlim", "ylim", "facet", "facet.args", "by", "xaxb", "yaxb", "null_by", "null_facet", "col", "bg", "axes", "xaxt", "yaxt")) ## process weights if (!is.null(weights)) { @@ -237,9 +237,6 @@ data_spineplot = function(off = NULL, breaks = NULL, xlevels = xlevels, ylevels # catch for x_by / y/by if (isTRUE(x_by)) datapoints$by = factor(rep(xaxlabels, each = ny)) # each x label extends over ny rows if (isTRUE(y_by)) datapoints$by = factor(rep_len(yaxlabels, nrow(datapoints))) - - ## grayscale flag - grayscale = null_by && null_palette && is.null(.tpar[["palette.qualitative"]]) x = c(datapoints$xmin, datapoints$xmax) y = c(datapoints$ymin, datapoints$ymax) @@ -275,7 +272,7 @@ data_spineplot = function(off = NULL, breaks = NULL, xlevels = xlevels, ylevels axes = axes_orig, xaxt = xaxt_orig, yaxt = yaxt_orig, - grayscale = grayscale, + null_by = null_by, x_by = x_by, y_by = y_by ) @@ -297,7 +294,6 @@ data_spineplot = function(off = NULL, breaks = NULL, xlevels = xlevels, ylevels return(fun) } -#' @importFrom grDevices gray.colors draw_spineplot = function(tol.ylab = 0.05, off = NULL, col = NULL, xaxlabels = NULL, yaxlabels = NULL) { fun = function(ixmin, iymin, ixmax, iymax, ilty, ilwd, icol, ibg, flip, @@ -314,7 +310,7 @@ draw_spineplot = function(tol.ylab = 0.05, off = NULL, col = NULL, xaxlabels = N nx = type_info[["nx"]] ny = type_info[["ny"]] x.categorical = type_info[["x.categorical"]] - grayscale = type_info[["grayscale"]] + null_by = type_info[["null_by"]] x_by = type_info[["x_by"]] y_by = type_info[["y_by"]] @@ -322,7 +318,16 @@ draw_spineplot = function(tol.ylab = 0.05, off = NULL, col = NULL, xaxlabels = N if (is.null(col)) { if (is.null(ibg)) ibg = icol if (isFALSE(y_by)) { - ibg = if (isTRUE(grayscale)) gray.colors(ny) else seq_palette(ibg, ny) + # For single-group displays, use a neutral grey ramp (gray.colors) + # whenever the resolved seed colour is achromatic (e.g. the black + # default of the plain default or the "bw"/"ipsum" themes), so these + # are consistent regardless of whether a palette is declared -- the + # same principle as the single-group fill logic in by_bg(). For grouped + # displays we never switch to grayscale: each group (including one + # whose palette colour is black) follows the same seq_palette ramp so + # the fills stay in sync with the legend swatches. + gs = isTRUE(null_by) && is_achromatic(ibg) + ibg = seq_palette(ibg, ny, grayscale = gs) } ibg = rep_len(ibg, ny) } else { @@ -398,27 +403,3 @@ spine_axis = function(side, ..., type = "standard", categorical = TRUE) { do.call("axis", args) } } - -#' @importFrom grDevices col2rgb convertColor hcl -to_hcl = function(x) { - x = t(col2rgb(x, alpha = TRUE)/255) - alpha = x[, 4] - x = x[, 1:3] - x = convertColor(x, from = "sRGB", to = "Luv") - x = cbind(H = atan2(x[, 3L], x[, 2L]) * 180/pi, C = sqrt(x[, 2L]^2 + x[, 3L]^2), L = x[, 1L]) - x[is.na(x[, 1L]), 1L] = 0 - x[x[, 1L] < 0, 1L] = x[x[, 1L] < 0, 1L] + 360 - attr(x, "alpha") = alpha - return(x) -} - -seq_palette = function(x, n, power = 1.5) { - x = drop(to_hcl(x[1L])) - alpha = attr(x, "alpha") - hcl( - h = x[1L], - c = seq.int(from = x[2L]^(1/power), to = 0, length.out = n + 1)[1L:n]^power, - l = 100 - seq.int(from = (100 - x[3L])^(1/power), to = pmin(8, (100 - x[3L])/2)^(1/power), length.out = n)^power, - alpha = alpha - )[1L:n] -} diff --git a/R/type_violin.R b/R/type_violin.R index 7f7e6795..1c7847e7 100644 --- a/R/type_violin.R +++ b/R/type_violin.R @@ -126,8 +126,9 @@ data_violin = function(bw = "nrd0", adjust = 1, kernel = "gaussian", n = 512, } if (length(unique(datapoints[["by"]])) == 1 && null_palette) { - if (is.null(col)) col = par("fg") - if (is.null(bg)) bg = "lightgray" + # With a theme palette active, leave bg = NULL so the fill tracks + # the resolved border colour (see by_bg). Otherwise use neutral grey. + if (is.null(bg) && is.null(get_tpar("palette.qualitative", default = NULL))) bg = "lightgray" } else if (is.null(bg)) { bg = "by" } diff --git a/R/utils.R b/R/utils.R index 6afe6b06..43a283d8 100644 --- a/R/utils.R +++ b/R/utils.R @@ -217,3 +217,46 @@ restore_margin_inner = function(ooma, topmar_epsilon = 0.1) { par(omd = c(0, 1, 0, 1)) } } + + +# Convert colour(s) to HCL-like (Luv) coordinates, preserving alpha. Helper for +# seq_palette(). (Originally lived in type_spineplot.R.) +#' @importFrom grDevices col2rgb convertColor hcl +to_hcl = function(x) { + x = t(col2rgb(x, alpha = TRUE)/255) + alpha = x[, 4] + x = x[, 1:3] + x = convertColor(x, from = "sRGB", to = "Luv") + x = cbind(H = atan2(x[, 3L], x[, 2L]) * 180/pi, C = sqrt(x[, 2L]^2 + x[, 3L]^2), L = x[, 1L]) + x[is.na(x[, 1L]), 1L] = 0 + x[x[, 1L] < 0, 1L] = x[x[, 1L] < 0, 1L] + 360 + attr(x, "alpha") = alpha + return(x) +} + +# Is a colour (effectively) neutral grey, i.e. has near-zero chroma? Used to +# decide when a single-group fill should fall back to a plain grey rather than a +# coloured tint (see by_bg() and draw_spineplot()). Black, white, and greys are +# all achromatic; any palette hue is not. +is_achromatic = function(x, tol = 1) { + drop(to_hcl(x))[2L] < tol +} + +# Build an n-step sequential ramp from colour `x` toward near-white, in HCL +# space (reduces chroma, increases lightness). Used for single-group fills +# (boxplot/violin/barplot/histogram), ridge fills, legend swatches, and +# spineplot shading. `seq_palette(col, 3)[3]` is thus a lighter-but-*opaque* +# tint of `col`. When `grayscale = TRUE`, returns a neutral grey ramp via +# gray.colors() instead (used by spineplot when no colour grouping is active). +#' @importFrom grDevices gray.colors +seq_palette = function(x, n, power = 1.5, grayscale = FALSE) { + if (isTRUE(grayscale)) return(gray.colors(n)) + x = drop(to_hcl(x[1L])) + alpha = attr(x, "alpha") + hcl( + h = x[1L], + c = seq.int(from = x[2L]^(1/power), to = 0, length.out = n + 1)[1L:n]^power, + l = 100 - seq.int(from = (100 - x[3L])^(1/power), to = pmin(8, (100 - x[3L])/2)^(1/power), length.out = n)^power, + alpha = alpha + )[1L:n] +} diff --git a/inst/tinytest/_tinysnapshot/barplot_formula_univariate.svg b/inst/tinytest/_tinysnapshot/barplot_formula_univariate.svg index ecd38adc..4e096cb5 100644 --- a/inst/tinytest/_tinysnapshot/barplot_formula_univariate.svg +++ b/inst/tinytest/_tinysnapshot/barplot_formula_univariate.svg @@ -51,9 +51,9 @@ - - - + + + diff --git a/inst/tinytest/_tinysnapshot/barplot_formula_y1.svg b/inst/tinytest/_tinysnapshot/barplot_formula_y1.svg index ecd38adc..4e096cb5 100644 --- a/inst/tinytest/_tinysnapshot/barplot_formula_y1.svg +++ b/inst/tinytest/_tinysnapshot/barplot_formula_y1.svg @@ -51,9 +51,9 @@ - - - + + + diff --git a/inst/tinytest/_tinysnapshot/barplot_offset_flip.svg b/inst/tinytest/_tinysnapshot/barplot_offset_flip.svg index 627db276..ef4ad340 100644 --- a/inst/tinytest/_tinysnapshot/barplot_offset_flip.svg +++ b/inst/tinytest/_tinysnapshot/barplot_offset_flip.svg @@ -49,9 +49,9 @@ - - - + + + diff --git a/inst/tinytest/_tinysnapshot/barplot_offset_scalar.svg b/inst/tinytest/_tinysnapshot/barplot_offset_scalar.svg index 17d6d75b..34174ca7 100644 --- a/inst/tinytest/_tinysnapshot/barplot_offset_scalar.svg +++ b/inst/tinytest/_tinysnapshot/barplot_offset_scalar.svg @@ -60,16 +60,16 @@ - - - - - - - - - - + + + + + + + + + + diff --git a/inst/tinytest/_tinysnapshot/barplot_offset_waterfall.svg b/inst/tinytest/_tinysnapshot/barplot_offset_waterfall.svg index a73e64c9..208bcde1 100644 --- a/inst/tinytest/_tinysnapshot/barplot_offset_waterfall.svg +++ b/inst/tinytest/_tinysnapshot/barplot_offset_waterfall.svg @@ -50,10 +50,10 @@ - - - - + + + + diff --git a/inst/tinytest/_tinysnapshot/barplot_simple.svg b/inst/tinytest/_tinysnapshot/barplot_simple.svg index 224799fd..d3383b2a 100644 --- a/inst/tinytest/_tinysnapshot/barplot_simple.svg +++ b/inst/tinytest/_tinysnapshot/barplot_simple.svg @@ -55,9 +55,9 @@ - - - + + + diff --git a/inst/tinytest/_tinysnapshot/barplot_text_issue469.svg b/inst/tinytest/_tinysnapshot/barplot_text_issue469.svg index e8131eea..fd88909b 100644 --- a/inst/tinytest/_tinysnapshot/barplot_text_issue469.svg +++ b/inst/tinytest/_tinysnapshot/barplot_text_issue469.svg @@ -55,9 +55,9 @@ - - - + + + 11 diff --git a/inst/tinytest/_tinysnapshot/barplot_xlevels_issue430.svg b/inst/tinytest/_tinysnapshot/barplot_xlevels_issue430.svg index 26076ab1..bd58dafc 100644 --- a/inst/tinytest/_tinysnapshot/barplot_xlevels_issue430.svg +++ b/inst/tinytest/_tinysnapshot/barplot_xlevels_issue430.svg @@ -55,9 +55,9 @@ - - - + + + diff --git a/inst/tinytest/_tinysnapshot/facet_hist_3x2.svg b/inst/tinytest/_tinysnapshot/facet_hist_3x2.svg index 9c8098d4..8e474f72 100644 --- a/inst/tinytest/_tinysnapshot/facet_hist_3x2.svg +++ b/inst/tinytest/_tinysnapshot/facet_hist_3x2.svg @@ -244,29 +244,29 @@ - - + + - - - + + + - + - + - - - - + + + + - - + + diff --git a/inst/tinytest/_tinysnapshot/formula_y1_cust_lab.svg b/inst/tinytest/_tinysnapshot/formula_y1_cust_lab.svg index 27aaa9bb..b2a09ac4 100644 --- a/inst/tinytest/_tinysnapshot/formula_y1_cust_lab.svg +++ b/inst/tinytest/_tinysnapshot/formula_y1_cust_lab.svg @@ -62,14 +62,14 @@ - - - - - - - - + + + + + + + + diff --git a/inst/tinytest/_tinysnapshot/hist_facet_free.svg b/inst/tinytest/_tinysnapshot/hist_facet_free.svg index 75556111..5f2eb9d7 100644 --- a/inst/tinytest/_tinysnapshot/hist_facet_free.svg +++ b/inst/tinytest/_tinysnapshot/hist_facet_free.svg @@ -150,25 +150,25 @@ - - - + + + - - - - - + + + + + - - - - - - - + + + + + + + diff --git a/inst/tinytest/_tinysnapshot/hist_facet_free_breaks_free.svg b/inst/tinytest/_tinysnapshot/hist_facet_free_breaks_free.svg index 6ca60a1d..16bdb963 100644 --- a/inst/tinytest/_tinysnapshot/hist_facet_free_breaks_free.svg +++ b/inst/tinytest/_tinysnapshot/hist_facet_free_breaks_free.svg @@ -152,29 +152,29 @@ - - - - - + + + + + - - - - - - - - + + + + + + + + - - - - - - + + + + + + diff --git a/inst/tinytest/_tinysnapshot/hist_faceted.svg b/inst/tinytest/_tinysnapshot/hist_faceted.svg index 62267397..f16d3afc 100644 --- a/inst/tinytest/_tinysnapshot/hist_faceted.svg +++ b/inst/tinytest/_tinysnapshot/hist_faceted.svg @@ -138,24 +138,24 @@ - - + + - - - - - - + + + + + + - - - - - - + + + + + + diff --git a/inst/tinytest/_tinysnapshot/hist_formula_y1.svg b/inst/tinytest/_tinysnapshot/hist_formula_y1.svg index 599d843d..25eb1045 100644 --- a/inst/tinytest/_tinysnapshot/hist_formula_y1.svg +++ b/inst/tinytest/_tinysnapshot/hist_formula_y1.svg @@ -62,14 +62,14 @@ - - - - - - - - + + + + + + + + diff --git a/inst/tinytest/_tinysnapshot/hist_simple.svg b/inst/tinytest/_tinysnapshot/hist_simple.svg index 2b256a7b..3726ce37 100644 --- a/inst/tinytest/_tinysnapshot/hist_simple.svg +++ b/inst/tinytest/_tinysnapshot/hist_simple.svg @@ -62,15 +62,15 @@ - - - - - - - - - + + + + + + + + + diff --git a/inst/tinytest/_tinysnapshot/hist_simple_white.svg b/inst/tinytest/_tinysnapshot/hist_simple_white.svg index 46a9dacf..35b95245 100644 --- a/inst/tinytest/_tinysnapshot/hist_simple_white.svg +++ b/inst/tinytest/_tinysnapshot/hist_simple_white.svg @@ -62,15 +62,15 @@ - - - - - - - - - + + + + + + + + + diff --git a/inst/tinytest/_tinysnapshot/issue_545_xaxs_yaxs_restoration.svg b/inst/tinytest/_tinysnapshot/issue_545_xaxs_yaxs_restoration.svg index 18f12716..437f1451 100644 --- a/inst/tinytest/_tinysnapshot/issue_545_xaxs_yaxs_restoration.svg +++ b/inst/tinytest/_tinysnapshot/issue_545_xaxs_yaxs_restoration.svg @@ -63,9 +63,9 @@ - - - + + + diff --git a/inst/tinytest/_tinysnapshot/ridge_gradient_white_facet_theme_ridge.svg b/inst/tinytest/_tinysnapshot/ridge_gradient_white_facet_theme_ridge.svg new file mode 100644 index 00000000..7a3291e3 --- /dev/null +++ b/inst/tinytest/_tinysnapshot/ridge_gradient_white_facet_theme_ridge.svg @@ -0,0 +1,2172 @@ + + + + + + + + + + + + + +mpg +am + + + + + + + + + + + + + + + + + + +5 +10 +15 +20 +25 +30 +35 +40 + +0 + + + + + + + + + + + + + + + + + + + +5 +10 +15 +20 +25 +30 +35 +40 + +1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +0 +1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +0 +1 + + + + + + + + + + diff --git a/inst/tinytest/_tinysnapshot/ridge_theme_palette.svg b/inst/tinytest/_tinysnapshot/ridge_theme_palette.svg index e029639a..3d71d702 100644 --- a/inst/tinytest/_tinysnapshot/ridge_theme_palette.svg +++ b/inst/tinytest/_tinysnapshot/ridge_theme_palette.svg @@ -58,11 +58,11 @@ - + - + - + setosa diff --git a/inst/tinytest/_tinysnapshot/text_labeller_percent.svg b/inst/tinytest/_tinysnapshot/text_labeller_percent.svg index c8822428..ed0de01a 100644 --- a/inst/tinytest/_tinysnapshot/text_labeller_percent.svg +++ b/inst/tinytest/_tinysnapshot/text_labeller_percent.svg @@ -50,8 +50,8 @@ - - + + 50% 80% diff --git a/inst/tinytest/_tinysnapshot/tinytheme_broadsheet.svg b/inst/tinytest/_tinysnapshot/tinytheme_broadsheet.svg index ce08192e..9d94a938 100644 --- a/inst/tinytest/_tinysnapshot/tinytheme_broadsheet.svg +++ b/inst/tinytest/_tinysnapshot/tinytheme_broadsheet.svg @@ -26,8 +26,8 @@ - - + + factor(am) 0 1 @@ -74,38 +74,38 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inst/tinytest/_tinysnapshot/tinytheme_bw.svg b/inst/tinytest/_tinysnapshot/tinytheme_bw.svg index 6f793ac8..d061d640 100644 --- a/inst/tinytest/_tinysnapshot/tinytheme_bw.svg +++ b/inst/tinytest/_tinysnapshot/tinytheme_bw.svg @@ -26,8 +26,8 @@ - - + + factor(am) 0 1 @@ -100,38 +100,38 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inst/tinytest/_tinysnapshot/tinytheme_classic.svg b/inst/tinytest/_tinysnapshot/tinytheme_classic.svg index d3c2a128..f813f232 100644 --- a/inst/tinytest/_tinysnapshot/tinytheme_classic.svg +++ b/inst/tinytest/_tinysnapshot/tinytheme_classic.svg @@ -26,8 +26,8 @@ - - + + factor(am) 0 1 @@ -76,38 +76,38 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inst/tinytest/_tinysnapshot/tinytheme_dynamic_boxplot_flip.svg b/inst/tinytest/_tinysnapshot/tinytheme_dynamic_boxplot_flip.svg index 5714b287..2b02605e 100644 --- a/inst/tinytest/_tinysnapshot/tinytheme_dynamic_boxplot_flip.svg +++ b/inst/tinytest/_tinysnapshot/tinytheme_dynamic_boxplot_flip.svg @@ -79,51 +79,51 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inst/tinytest/_tinysnapshot/tinytheme_dynamic_x_boxplot.svg b/inst/tinytest/_tinysnapshot/tinytheme_dynamic_x_boxplot.svg index b02c2e41..9a33ee38 100644 --- a/inst/tinytest/_tinysnapshot/tinytheme_dynamic_x_boxplot.svg +++ b/inst/tinytest/_tinysnapshot/tinytheme_dynamic_x_boxplot.svg @@ -79,51 +79,51 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inst/tinytest/_tinysnapshot/tinytheme_dynamic_yaxl.svg b/inst/tinytest/_tinysnapshot/tinytheme_dynamic_yaxl.svg index e8ca35f8..f2aab904 100644 --- a/inst/tinytest/_tinysnapshot/tinytheme_dynamic_yaxl.svg +++ b/inst/tinytest/_tinysnapshot/tinytheme_dynamic_yaxl.svg @@ -79,65 +79,65 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inst/tinytest/_tinysnapshot/tinytheme_ipsum2.svg b/inst/tinytest/_tinysnapshot/tinytheme_ipsum2.svg index 7e5f97c0..860d47b8 100644 --- a/inst/tinytest/_tinysnapshot/tinytheme_ipsum2.svg +++ b/inst/tinytest/_tinysnapshot/tinytheme_ipsum2.svg @@ -26,8 +26,8 @@ - - + + factor(am) 0 1 @@ -86,38 +86,38 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inst/tinytest/_tinysnapshot/tinytheme_linedraw.svg b/inst/tinytest/_tinysnapshot/tinytheme_linedraw.svg index ac1547ce..04112b7f 100644 --- a/inst/tinytest/_tinysnapshot/tinytheme_linedraw.svg +++ b/inst/tinytest/_tinysnapshot/tinytheme_linedraw.svg @@ -26,8 +26,8 @@ - - + + factor(am) 0 1 @@ -100,38 +100,38 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inst/tinytest/_tinysnapshot/tinytheme_minimal.svg b/inst/tinytest/_tinysnapshot/tinytheme_minimal.svg index ecc39d3d..f4df4e75 100644 --- a/inst/tinytest/_tinysnapshot/tinytheme_minimal.svg +++ b/inst/tinytest/_tinysnapshot/tinytheme_minimal.svg @@ -26,8 +26,8 @@ - - + + factor(am) 0 1 @@ -86,38 +86,38 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inst/tinytest/_tinysnapshot/tinytheme_register_float2.svg b/inst/tinytest/_tinysnapshot/tinytheme_register_float2.svg index 0f7523e6..c7c3d758 100644 --- a/inst/tinytest/_tinysnapshot/tinytheme_register_float2.svg +++ b/inst/tinytest/_tinysnapshot/tinytheme_register_float2.svg @@ -69,11 +69,11 @@ - - - - - + + + + + diff --git a/inst/tinytest/_tinysnapshot/tinytheme_single_basic.svg b/inst/tinytest/_tinysnapshot/tinytheme_single_basic.svg new file mode 100644 index 00000000..5772d2bb --- /dev/null +++ b/inst/tinytest/_tinysnapshot/tinytheme_single_basic.svg @@ -0,0 +1,110 @@ + + + + + + + + + + + + + +tinytheme("basic") +Title of the plot +hp +mpg + + + + + + + +50 +100 +150 +200 +250 +300 + + + + + + +10 +15 +20 +25 +30 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inst/tinytest/_tinysnapshot/tinytheme_single_broadsheet.svg b/inst/tinytest/_tinysnapshot/tinytheme_single_broadsheet.svg new file mode 100644 index 00000000..19cea57e --- /dev/null +++ b/inst/tinytest/_tinysnapshot/tinytheme_single_broadsheet.svg @@ -0,0 +1,97 @@ + + + + + + + + + + + + + +tinytheme("broadsheet") +Title of the plot +hp +mpg + + + + + + + +50 +100 +150 +200 +250 +300 +10 +15 +20 +25 +30 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inst/tinytest/_tinysnapshot/tinytheme_single_bw.svg b/inst/tinytest/_tinysnapshot/tinytheme_single_bw.svg new file mode 100644 index 00000000..241e3e48 --- /dev/null +++ b/inst/tinytest/_tinysnapshot/tinytheme_single_bw.svg @@ -0,0 +1,123 @@ + + + + + + + + + + + + + +tinytheme("bw") +Title of the plot +hp +mpg + + + + + + + +50 +100 +150 +200 +250 +300 + + + + + + +10 +15 +20 +25 +30 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inst/tinytest/_tinysnapshot/tinytheme_single_classic.svg b/inst/tinytest/_tinysnapshot/tinytheme_single_classic.svg new file mode 100644 index 00000000..5996f169 --- /dev/null +++ b/inst/tinytest/_tinysnapshot/tinytheme_single_classic.svg @@ -0,0 +1,99 @@ + + + + + + + + + + + + + +tinytheme("classic") +Title of the plot +hp +mpg + + + + + + + +50 +100 +150 +200 +250 +300 + + + + + + +10 +15 +20 +25 +30 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inst/tinytest/_tinysnapshot/tinytheme_single_clean.svg b/inst/tinytest/_tinysnapshot/tinytheme_single_clean.svg new file mode 100644 index 00000000..89748a45 --- /dev/null +++ b/inst/tinytest/_tinysnapshot/tinytheme_single_clean.svg @@ -0,0 +1,110 @@ + + + + + + + + + + + + + +tinytheme("clean") +Title of the plot +hp +mpg + + + + + + + +50 +100 +150 +200 +250 +300 + + + + + + +10 +15 +20 +25 +30 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inst/tinytest/_tinysnapshot/tinytheme_single_clean2.svg b/inst/tinytest/_tinysnapshot/tinytheme_single_clean2.svg new file mode 100644 index 00000000..1a519871 --- /dev/null +++ b/inst/tinytest/_tinysnapshot/tinytheme_single_clean2.svg @@ -0,0 +1,96 @@ + + + + + + + + + + + + + +tinytheme("clean2") +Title of the plot +hp +mpg +50 +100 +150 +200 +250 +300 +10 +15 +20 +25 +30 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inst/tinytest/_tinysnapshot/tinytheme_single_dark.svg b/inst/tinytest/_tinysnapshot/tinytheme_single_dark.svg new file mode 100644 index 00000000..8a435403 --- /dev/null +++ b/inst/tinytest/_tinysnapshot/tinytheme_single_dark.svg @@ -0,0 +1,109 @@ + + + + + + + + + + + + + +tinytheme("dark") +Title of the plot +hp +mpg +50 +100 +150 +200 +250 +300 +10 +15 +20 +25 +30 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inst/tinytest/_tinysnapshot/tinytheme_single_default.svg b/inst/tinytest/_tinysnapshot/tinytheme_single_default.svg new file mode 100644 index 00000000..6ae66bf6 --- /dev/null +++ b/inst/tinytest/_tinysnapshot/tinytheme_single_default.svg @@ -0,0 +1,99 @@ + + + + + + + + + + + + + +tinytheme("default") +Title of the plot +hp +mpg + + + + + + + +50 +100 +150 +200 +250 +300 + + + + + + +10 +15 +20 +25 +30 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inst/tinytest/_tinysnapshot/tinytheme_single_dynamic.svg b/inst/tinytest/_tinysnapshot/tinytheme_single_dynamic.svg new file mode 100644 index 00000000..57cb3782 --- /dev/null +++ b/inst/tinytest/_tinysnapshot/tinytheme_single_dynamic.svg @@ -0,0 +1,99 @@ + + + + + + + + + + + + + +tinytheme("dynamic") +Title of the plot +hp +mpg + + + + + + + +50 +100 +150 +200 +250 +300 + + + + + + +10 +15 +20 +25 +30 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inst/tinytest/_tinysnapshot/tinytheme_single_float.svg b/inst/tinytest/_tinysnapshot/tinytheme_single_float.svg new file mode 100644 index 00000000..20ef6c83 --- /dev/null +++ b/inst/tinytest/_tinysnapshot/tinytheme_single_float.svg @@ -0,0 +1,98 @@ + + + + + + + + + + + + + +tinytheme("float") +Title of the plot +hp +mpg + + + + + + + +50 +100 +150 +200 +250 +300 + + + + + + +10 +15 +20 +25 +30 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inst/tinytest/_tinysnapshot/tinytheme_single_ipsum.svg b/inst/tinytest/_tinysnapshot/tinytheme_single_ipsum.svg new file mode 100644 index 00000000..286e4397 --- /dev/null +++ b/inst/tinytest/_tinysnapshot/tinytheme_single_ipsum.svg @@ -0,0 +1,109 @@ + + + + + + + + + + + + + +tinytheme("ipsum") +Title of the plot +hp +mpg +50 +100 +150 +200 +250 +300 +10 +15 +20 +25 +30 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inst/tinytest/_tinysnapshot/tinytheme_single_ipsum2.svg b/inst/tinytest/_tinysnapshot/tinytheme_single_ipsum2.svg new file mode 100644 index 00000000..b0019f70 --- /dev/null +++ b/inst/tinytest/_tinysnapshot/tinytheme_single_ipsum2.svg @@ -0,0 +1,109 @@ + + + + + + + + + + + + + +tinytheme("ipsum2") +Title of the plot +hp +mpg +50 +100 +150 +200 +250 +300 +10 +15 +20 +25 +30 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inst/tinytest/_tinysnapshot/tinytheme_single_linedraw.svg b/inst/tinytest/_tinysnapshot/tinytheme_single_linedraw.svg new file mode 100644 index 00000000..1aeb1a89 --- /dev/null +++ b/inst/tinytest/_tinysnapshot/tinytheme_single_linedraw.svg @@ -0,0 +1,123 @@ + + + + + + + + + + + + + +tinytheme("linedraw") +Title of the plot +hp +mpg + + + + + + + +50 +100 +150 +200 +250 +300 + + + + + + +10 +15 +20 +25 +30 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inst/tinytest/_tinysnapshot/tinytheme_single_minimal.svg b/inst/tinytest/_tinysnapshot/tinytheme_single_minimal.svg new file mode 100644 index 00000000..32deb0ed --- /dev/null +++ b/inst/tinytest/_tinysnapshot/tinytheme_single_minimal.svg @@ -0,0 +1,109 @@ + + + + + + + + + + + + + +tinytheme("minimal") +Title of the plot +hp +mpg +50 +100 +150 +200 +250 +300 +10 +15 +20 +25 +30 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inst/tinytest/_tinysnapshot/tinytheme_single_nber.svg b/inst/tinytest/_tinysnapshot/tinytheme_single_nber.svg new file mode 100644 index 00000000..f7ab7088 --- /dev/null +++ b/inst/tinytest/_tinysnapshot/tinytheme_single_nber.svg @@ -0,0 +1,97 @@ + + + + + + + + + + + + + +tinytheme("nber") +Title of the plot +hp +mpg + + + + + + + +50 +100 +150 +200 +250 +300 +10 +15 +20 +25 +30 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inst/tinytest/_tinysnapshot/tinytheme_single_ridge.svg b/inst/tinytest/_tinysnapshot/tinytheme_single_ridge.svg new file mode 100644 index 00000000..4509e2ab --- /dev/null +++ b/inst/tinytest/_tinysnapshot/tinytheme_single_ridge.svg @@ -0,0 +1,99 @@ + + + + + + + + + + + + + +tinytheme("ridge") +Title of the plot +hp +mpg + + + + + + + +50 +100 +150 +200 +250 +300 + + + + + + +10 +15 +20 +25 +30 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inst/tinytest/_tinysnapshot/tinytheme_single_ridge2.svg b/inst/tinytest/_tinysnapshot/tinytheme_single_ridge2.svg new file mode 100644 index 00000000..860190da --- /dev/null +++ b/inst/tinytest/_tinysnapshot/tinytheme_single_ridge2.svg @@ -0,0 +1,85 @@ + + + + + + + + + + + + + +tinytheme("ridge2") +Title of the plot +hp +mpg +50 +100 +150 +200 +250 +300 +10 +15 +20 +25 +30 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inst/tinytest/_tinysnapshot/tinytheme_single_socviz.svg b/inst/tinytest/_tinysnapshot/tinytheme_single_socviz.svg new file mode 100644 index 00000000..ff3623b7 --- /dev/null +++ b/inst/tinytest/_tinysnapshot/tinytheme_single_socviz.svg @@ -0,0 +1,123 @@ + + + + + + + + + + + + + +tinytheme("socviz") +Title of the plot +hp +mpg + + + + + + + +50 +100 +150 +200 +250 +300 + + + + + + +10 +15 +20 +25 +30 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inst/tinytest/_tinysnapshot/tinytheme_single_tufte.svg b/inst/tinytest/_tinysnapshot/tinytheme_single_tufte.svg new file mode 100644 index 00000000..c979719e --- /dev/null +++ b/inst/tinytest/_tinysnapshot/tinytheme_single_tufte.svg @@ -0,0 +1,126 @@ + + + + + + + + + + + + + +tinytheme("tufte") +Title of the plot +hp +mpg + + + + + + + + + + + + + + + + +60 +80 +100 +140 +180 +220 +260 +300 +340 + + + + + + + + + + + + + + +10 +12 +14 +16 +18 +20 +22 +24 +26 +28 +30 +32 +34 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inst/tinytest/_tinysnapshot/tinytheme_single_void.svg b/inst/tinytest/_tinysnapshot/tinytheme_single_void.svg new file mode 100644 index 00000000..39d583f5 --- /dev/null +++ b/inst/tinytest/_tinysnapshot/tinytheme_single_void.svg @@ -0,0 +1,74 @@ + + + + + + + + + + + + + +tinytheme("void") +Title of the plot +hp +mpg + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inst/tinytest/_tinysnapshot/tinytheme_single_web.svg b/inst/tinytest/_tinysnapshot/tinytheme_single_web.svg new file mode 100644 index 00000000..db17b54e --- /dev/null +++ b/inst/tinytest/_tinysnapshot/tinytheme_single_web.svg @@ -0,0 +1,96 @@ + + + + + + + + + + + + + +tinytheme("web") +Title of the plot +hp +mpg +50 +100 +150 +200 +250 +300 +10 +15 +20 +25 +30 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inst/tinytest/test-tinytheme.R b/inst/tinytest/test-tinytheme.R index e63f63e6..7c6491e0 100644 --- a/inst/tinytest/test-tinytheme.R +++ b/inst/tinytest/test-tinytheme.R @@ -16,6 +16,19 @@ for (thm in thms) { } rm(thm) +# Single-group (no `by`) displays: safeguard the per-theme default colour +# logic, i.e. col.default and the leading-black palette drop. (#598) +for (thm in thms) { + tinytheme(thm) + f = function() tinyplot( + mpg ~ hp, data = mtcars, + main = "Title of the plot", + sub = paste0('tinytheme("', thm, '")') + ) + expect_snapshot_plot(f, label = paste0("tinytheme_single_", thm)) +} +rm(thm) + # legend placement f = function() { diff --git a/inst/tinytest/test-type_ridge.R b/inst/tinytest/test-type_ridge.R index eef900fc..ca8d8c77 100644 --- a/inst/tinytest/test-type_ridge.R +++ b/inst/tinytest/test-type_ridge.R @@ -118,8 +118,7 @@ expect_snapshot_plot(f, label = "ridge_gradient_probs") f = function() { tinyplot( am ~ mpg, facet = ~vs, data = mtcars, - type = type_ridge(gradient = "agsunset"), - col = "white" + type = type_ridge(gradient = "agsunset") ) } expect_snapshot_plot(f, label = "ridge_gradient_facet") @@ -129,22 +128,23 @@ tinytheme("ridge2") expect_snapshot_plot(f, label = "ridge_gradient_facet_theme_ridge2") tinytheme() +# Dedicated test for a white boundary color between gradient ridges, which is a +# common aesthetic for separating overlapping densities. (#598) f = function() { tinyplot( am ~ mpg, facet = ~vs, data = mtcars, - type = type_ridge(gradient = "agsunset", raster = TRUE, alpha = 0.5), - col = "white" + type = type_ridge(gradient = TRUE), + col = "white", + theme = "ridge" ) } -expect_snapshot_plot(f, label = "ridge_gradient_facet_raster_alpha") - +expect_snapshot_plot(f, label = "ridge_gradient_white_facet_theme_ridge") f = function() { tinyplot( am ~ mpg, facet = ~vs, data = mtcars, - type = type_ridge(gradient = "agsunset", raster = TRUE, alpha = 0.5), - col = "white" + type = type_ridge(gradient = "agsunset", raster = TRUE, alpha = 0.5) ) } expect_snapshot_plot(f, label = "ridge_gradient_facet_raster_alpha") diff --git a/man/tpar.Rd b/man/tpar.Rd index ccfe92a9..eb2b966c 100644 --- a/man/tpar.Rd +++ b/man/tpar.Rd @@ -66,6 +66,7 @@ you should rather use \code{par()} instead. \item \code{cairo}: Logical indicating whether \code{\link[grDevices]{cairo_pdf}} should be used when writing plots to PDF. If \code{FALSE}, then \code{\link[grDevices]{pdf}} will be used instead, with implications for embedding (non-standard) fonts. Only used if \code{tinyplot(..., file = ".pdf")} is called. Defaults to the value of \code{capabilities("cairo")}. \item \code{cex.cap}: Numeric expansion factor for the plot caption text. Defaults to \code{1} for the default, basic, and dynamic themes, and \code{0.8} for clean/classic and their descendants. \item \code{col.cap}: Character specifying the colour of the plot caption. Defaults to \code{"black"}. +\item \code{col.default}: Default colour for single-group displays (i.e. plots without a \code{by} grouping). Can be \code{NULL}, a length-1 character colour, or a length-1 numeric index into \code{palette.qualitative}. Defaults to \code{NULL}, in which case the first colour of the active qualitative palette is used (or base \code{palette()[1]}, typically black, if no theme is set). A character value sets the single-group colour independently of the multi-group palette. A numeric value \code{i} selects the \code{i}th colour of \code{palette.qualitative} as the single-group default; a \emph{negative} index additionally drops that colour from the palette used for grouped plots. For example, \code{col.default = -1} paired with \code{palette.qualitative = "Okabe-Ito"} uses black (the leading palette colour) for single-group plots and an Okabe-Ito-minus-black palette for grouped plots, avoiding the need to maintain a separate "no-black" palette. \item \code{font.cap}: Integer specifying the font face for the plot caption (\code{1} = plain, \code{2} = bold, \code{3} = italic, \code{4} = bold italic). Defaults to \code{1}. \item \code{line.cap}: Numeric specifying the margin line on which to draw the caption. If \code{NULL} (default), computed automatically based on the available bottom margin. \item \code{dynmar}: Logical indicating whether \code{tinyplot} should attempt dynamic adjustment of margins to reduce whitespace and/or account for spacing of text elements (e.g., long horizontal y-axis labels). Note that this parameter is tightly coupled to internal \code{tinythemes()} logic and should \emph{not} be adjusted manually unless you really know what you are doing or don't mind risking unintended consequences to your plot. diff --git a/man/type_ridge.Rd b/man/type_ridge.Rd index b8e2ec6a..4ceb9de4 100644 --- a/man/type_ridge.Rd +++ b/man/type_ridge.Rd @@ -99,7 +99,12 @@ section below.} of the ridge densities. Note that a singular value is expected; if multiple colors are provided then only the first will be used. This argument is mostly useful for the aesthetic effect of drawing a common outline color in -combination with gradient fills. See Examples.} +combination with gradient fills. See Examples. If left unset here, the +outline color can also be supplied via the top-level \code{tinyplot(..., col =)} +call (the \code{type_ridge(col =)} argument takes precedence if both are given). +When neither is supplied, the outline defaults to the active theme's +\code{col.default} (see \link{tpar}), falling back to the first color of the theme's +qualitative palette, or black if no theme is set.} \item{alpha}{Numeric in the range \verb{[0,1]} for adjusting the alpha transparency of the density fills. In most cases, will default to a value of