diff --git a/NEWS.md b/NEWS.md index 5a33eeb4..16ebcbe2 100644 --- a/NEWS.md +++ b/NEWS.md @@ -6,6 +6,12 @@ where the formatting is also better._ ## Development +### New features + +- Improved horizontal legend spacing, as well as multicolumn legend support. A + new example in the "Tips & tricks" vignettes demonstrates the latter. + (#446 @grantmcdermott) + ### Internals - Move `altdoc` from `Suggests` to `Config/Needs/website`. diff --git a/R/draw_legend.R b/R/draw_legend.R index ce960e6b..84aeb5f1 100644 --- a/R/draw_legend.R +++ b/R/draw_legend.R @@ -214,6 +214,10 @@ draw_legend = function( ) legend_args[["legend"]] = lgnd_labs } + + if (isTRUE(gradient)) { + legend_args[["ncol"]] = NULL + } # ## legend placement ---- @@ -329,24 +333,39 @@ draw_legend = function( } } - legend_args[["horiz"]] = TRUE + # enforce horizontal legend if user hasn't specified ncol arg + # (exception: gradient legends at bottom/top are always horizontal) + if (is.null(legend_args[["ncol"]]) || gradient) legend_args[["horiz"]] = TRUE - # Catch for horizontal ribbon legend spacing - if (type=="ribbon" && isTRUE(legend_args[["horiz"]])) { + } else { + + legend_args[["inset"]] = 0 + if (new_plot && draw) plot.new() + + } + + # Additional tweaks for horiz and/or multi-column legends + if (isTRUE(legend_args[["horiz"]]) || !is.null(legend_args[["ncol"]])) { + # tighter horizontal labelling + # See: https://github.com/grantmcdermott/tinyplot/issues/434 + if (!gradient) { + legend_args[["text.width"]] = NA + # Add a space to all labs except the outer most right ones + nlabs = length(legend_args[["legend"]]) + nidx = nlabs + if (!is.null(legend_args[["ncol"]])) nidx = tail(1:nlabs, (nlabs %/% legend_args[["ncol"]])) + legend_args[["legend"]][-nidx] = paste(legend_args[["legend"]][-nidx], " ") + } + # catch for horizontal ribbon legend spacing + if (type=="ribbon") { if (legend_args[["pt.lwd"]] == 1) { legend_args[["x.intersp"]] = 1 } else { legend_args[["x.intersp"]] = 0.5 } - } else if (gradient && isTRUE(legend_args[["horiz"]])) { + } else if (gradient) { legend_args[["x.intersp"]] = 0.5 } - - } else { - - legend_args[["inset"]] = 0 - if (new_plot && draw) plot.new() - } # diff --git a/inst/tinytest/_tinysnapshot/density_legend_bottom.svg b/inst/tinytest/_tinysnapshot/density_legend_bottom.svg index dacb7d94..c9088eb3 100644 --- a/inst/tinytest/_tinysnapshot/density_legend_bottom.svg +++ b/inst/tinytest/_tinysnapshot/density_legend_bottom.svg @@ -26,11 +26,11 @@ - - + + am -0 -1 +0 +1 diff --git a/inst/tinytest/_tinysnapshot/legend_default_legend.svg b/inst/tinytest/_tinysnapshot/legend_default_legend.svg index fed1ab42..82de1245 100644 --- a/inst/tinytest/_tinysnapshot/legend_default_legend.svg +++ b/inst/tinytest/_tinysnapshot/legend_default_legend.svg @@ -26,18 +26,18 @@ - - - + + + - - + + Month of the year -5 -6 -7 -8 -9 +5 +6 +7 +8 +9 diff --git a/inst/tinytest/_tinysnapshot/legend_formula_legend.svg b/inst/tinytest/_tinysnapshot/legend_formula_legend.svg index fed1ab42..82de1245 100644 --- a/inst/tinytest/_tinysnapshot/legend_formula_legend.svg +++ b/inst/tinytest/_tinysnapshot/legend_formula_legend.svg @@ -26,18 +26,18 @@ - - - + + + - - + + Month of the year -5 -6 -7 -8 -9 +5 +6 +7 +8 +9 diff --git a/inst/tinytest/_tinysnapshot/legend_keyword_default.svg b/inst/tinytest/_tinysnapshot/legend_keyword_default.svg index 7807b4fc..39bb588a 100644 --- a/inst/tinytest/_tinysnapshot/legend_keyword_default.svg +++ b/inst/tinytest/_tinysnapshot/legend_keyword_default.svg @@ -26,17 +26,17 @@ - - + + - - + + Month -5 -6 -7 -8 -9 +5 +6 +7 +8 +9 diff --git a/inst/tinytest/_tinysnapshot/legend_keyword_formula.svg b/inst/tinytest/_tinysnapshot/legend_keyword_formula.svg index 7807b4fc..39bb588a 100644 --- a/inst/tinytest/_tinysnapshot/legend_keyword_formula.svg +++ b/inst/tinytest/_tinysnapshot/legend_keyword_formula.svg @@ -26,17 +26,17 @@ - - + + - - + + Month -5 -6 -7 -8 -9 +5 +6 +7 +8 +9 diff --git a/inst/tinytest/_tinysnapshot/legend_keyword_outerbottom.svg b/inst/tinytest/_tinysnapshot/legend_keyword_outerbottom.svg index 7807b4fc..39bb588a 100644 --- a/inst/tinytest/_tinysnapshot/legend_keyword_outerbottom.svg +++ b/inst/tinytest/_tinysnapshot/legend_keyword_outerbottom.svg @@ -26,17 +26,17 @@ - - + + - - + + Month -5 -6 -7 -8 -9 +5 +6 +7 +8 +9 diff --git a/inst/tinytest/_tinysnapshot/legend_keyword_outertop.svg b/inst/tinytest/_tinysnapshot/legend_keyword_outertop.svg index de95ed57..4f514de5 100644 --- a/inst/tinytest/_tinysnapshot/legend_keyword_outertop.svg +++ b/inst/tinytest/_tinysnapshot/legend_keyword_outertop.svg @@ -26,17 +26,17 @@ - - + + - - + + Month -5 -6 -7 -8 -9 +5 +6 +7 +8 +9 diff --git a/inst/tinytest/_tinysnapshot/legend_lmar_bottom.svg b/inst/tinytest/_tinysnapshot/legend_lmar_bottom.svg index 092d655e..348e5bc7 100644 --- a/inst/tinytest/_tinysnapshot/legend_lmar_bottom.svg +++ b/inst/tinytest/_tinysnapshot/legend_lmar_bottom.svg @@ -26,18 +26,18 @@ - - - + + + - - + + Month -5 -6 -7 -8 -9 +5 +6 +7 +8 +9 diff --git a/inst/tinytest/_tinysnapshot/legend_lmar_top.svg b/inst/tinytest/_tinysnapshot/legend_lmar_top.svg index 4d7ce50e..2c1943a7 100644 --- a/inst/tinytest/_tinysnapshot/legend_lmar_top.svg +++ b/inst/tinytest/_tinysnapshot/legend_lmar_top.svg @@ -26,18 +26,18 @@ - - - + + + - - + + Month -5 -6 -7 -8 -9 +5 +6 +7 +8 +9 diff --git a/inst/tinytest/_tinysnapshot/legend_multiline_bottom.svg b/inst/tinytest/_tinysnapshot/legend_multiline_bottom.svg index 62c9f124..21efd3b5 100644 --- a/inst/tinytest/_tinysnapshot/legend_multiline_bottom.svg +++ b/inst/tinytest/_tinysnapshot/legend_multiline_bottom.svg @@ -26,21 +26,21 @@ - - - + + + - - + + Month of the year -5 -6 -7 -8 -9 +5 +6 +7 +8 +9 diff --git a/inst/tinytest/_tinysnapshot/legend_multiline_top.svg b/inst/tinytest/_tinysnapshot/legend_multiline_top.svg index e3e1542f..dadfc476 100644 --- a/inst/tinytest/_tinysnapshot/legend_multiline_top.svg +++ b/inst/tinytest/_tinysnapshot/legend_multiline_top.svg @@ -26,21 +26,21 @@ - - - + + + - - + + Month of the year -5 -6 -7 -8 -9 +5 +6 +7 +8 +9 diff --git a/inst/tinytest/_tinysnapshot/legend_spacing_horiz_label_bottom.svg b/inst/tinytest/_tinysnapshot/legend_spacing_horiz_label_bottom.svg new file mode 100644 index 00000000..14f28b3d --- /dev/null +++ b/inst/tinytest/_tinysnapshot/legend_spacing_horiz_label_bottom.svg @@ -0,0 +1,119 @@ + + + + + + + + + + + + + + + + + + +Month2 +May +June +July +August +September + + + + + + + +Day +Temp + + + + + + + + + + +0 +5 +10 +15 +20 +25 +30 + + + + + + + +65 +70 +75 +80 +85 +90 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inst/tinytest/_tinysnapshot/legend_spacing_ncol_bottom.svg b/inst/tinytest/_tinysnapshot/legend_spacing_ncol_bottom.svg new file mode 100644 index 00000000..8c2463a9 --- /dev/null +++ b/inst/tinytest/_tinysnapshot/legend_spacing_ncol_bottom.svg @@ -0,0 +1,224 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Chick +18 +16 +15 +13 +9 +20 +10 +8 +17 +19 +4 +6 +11 +3 +1 +12 +2 +5 +14 +7 +24 +30 +22 +23 +27 +28 +26 +25 +29 +21 +33 +37 +36 +31 +39 +38 +32 +40 +34 +35 +44 +45 +43 +41 +47 +49 +46 +50 +42 +48 + + + + + + + +Time +weight + + + + + + + + +0 +5 +10 +15 +20 + + + + + + + + +50 +100 +200 +300 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inst/tinytest/_tinysnapshot/legend_spacing_ncol_right.svg b/inst/tinytest/_tinysnapshot/legend_spacing_ncol_right.svg new file mode 100644 index 00000000..c3d77239 --- /dev/null +++ b/inst/tinytest/_tinysnapshot/legend_spacing_ncol_right.svg @@ -0,0 +1,327 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Chick +18 +16 +15 +13 +9 +20 +10 +8 +17 +19 +4 +6 +11 +3 +1 +12 +2 +5 +14 +7 +24 +30 +22 +23 +27 +28 +26 +25 +29 +21 +33 +37 +36 +31 +39 +38 +32 +40 +34 +35 +44 +45 +43 +41 +47 +49 +46 +50 +42 +48 + + + + + + + +Time +weight + + + + + + + + +0 +5 +10 +15 +20 + + + + + + + + +50 +100 +150 +200 +250 +300 +350 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inst/tinytest/_tinysnapshot/readme_legend_bottom.svg b/inst/tinytest/_tinysnapshot/readme_legend_bottom.svg index 0d73f9c2..80898069 100644 --- a/inst/tinytest/_tinysnapshot/readme_legend_bottom.svg +++ b/inst/tinytest/_tinysnapshot/readme_legend_bottom.svg @@ -26,18 +26,18 @@ - - - + + + - - + + Month of the year -5 -6 -7 -8 -9 +5 +6 +7 +8 +9 diff --git a/inst/tinytest/_tinysnapshot/restore_par_bottom.svg b/inst/tinytest/_tinysnapshot/restore_par_bottom.svg index f56a9ab6..98b6a146 100644 --- a/inst/tinytest/_tinysnapshot/restore_par_bottom.svg +++ b/inst/tinytest/_tinysnapshot/restore_par_bottom.svg @@ -26,14 +26,14 @@ - - - - + + + + Species -setosa -versicolor -virginica +setosa +versicolor +virginica diff --git a/inst/tinytest/_tinysnapshot/tinytheme_legend_bottom.svg b/inst/tinytest/_tinysnapshot/tinytheme_legend_bottom.svg index bf753938..3e2ce5de 100644 --- a/inst/tinytest/_tinysnapshot/tinytheme_legend_bottom.svg +++ b/inst/tinytest/_tinysnapshot/tinytheme_legend_bottom.svg @@ -26,11 +26,11 @@ - - + + factor(am) -0 -1 +0 +1 tinytheme("clean") + legend = "bottom!" diff --git a/inst/tinytest/test-legend.R b/inst/tinytest/test-legend.R index e2d15f44..cf72ece3 100644 --- a/inst/tinytest/test-legend.R +++ b/inst/tinytest/test-legend.R @@ -40,6 +40,28 @@ expect_snapshot_plot(f, label = "legend_keyword_outertopright") f = function() tinyplot(Temp ~ Day | Month, data = aq, legend = "bottomleft!") expect_snapshot_plot(f, label = "legend_keyword_outerbottomleft") +# Horizontal and/or multicolumn legend spacing + +aq$Month2 = factor(month.name[aq$Month], levels = month.name[5:9]) +f = function() tinyplot(Temp ~ Day | Month2, data = aq, legend = "bottom!") +expect_snapshot_plot(f, label = "legend_spacing_horiz_label_bottom") + +f = function() tinyplot( + weight ~ Time | Chick, + data = ChickWeight, + type = "ribbon", # not necessary for plot but helps to check some internal logic + legend = list("right!", ncol = 3) +) +expect_snapshot_plot(f, label = "legend_spacing_ncol_right") + +f = function() tinyplot( + weight ~ Time | Chick, + data = ChickWeight, + type = "l", + legend = list("bottom!", ncol = 5) +) +expect_snapshot_plot(f, label = "legend_spacing_ncol_bottom") + # Long legend titles f = function() tinyplot( diff --git a/vignettes/tips.qmd b/vignettes/tips.qmd index 63400d52..4f8cc026 100644 --- a/vignettes/tips.qmd +++ b/vignettes/tips.qmd @@ -13,11 +13,11 @@ package. Please feel free to suggest or add more tips via our [GitHub repo](http ## Legend -### Opacity (`alpha`) +### Legend transparency -By default, the legend inherits the opacity of the plotted elements. In some -cases, this may be undesirable. For example, in the following plot, the legend -is too light: +By default, the legend inherits the transparency (`alpha`) of the plotted +elements. In some cases, this may be undesirable. For example, in the following +plot, the legend is too light: ```{r} library(tinyplot) @@ -38,6 +38,37 @@ tinyplot(y ~ x | z, data = dat, pch = 19, empty = TRUE) tinyplot_add(empty = FALSE, alpha = .1) ``` +### Multicolumn legends + +For cases where we have many discrete groups, the default single column legend +can overrun the plot limits. For example: + +```{r} +library(tinyplot) + +plt(weight ~ Time | Chick, data = ChickWeight, type = "l") +``` + +To solve this undesirable behaviour, simply pass an approporiate `ncol` +adjustment as part of your legend (list) argument: + +```{r} +plt(weight ~ Time | Chick, data = ChickWeight, type = "l", + legend = list(ncol = 3)) +``` + +The same trick works for horizontal legends and/or legends in other positions, +as well for other plot types and themes. For example: + +```{r} +plt(weight ~ Time | Chick, data = ChickWeight, type = "l", + legend = list("bottom!", ncol = 5)) +``` + +(Admittedly, the end result is a bit compressed here because of the default +aspect ratio that we use for figures on our website. But for regular interactive +plots, or plots saved to disk, the aesthetic effect should be quite pleasing.) + ## Labels ### Direct labels