Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 22 additions & 1 deletion NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ visualizations.
logic. Recall, these are themes like `"dynamic"`, `"clean"`, `"bw"`, etc. that
automatically adjust margin spacing and related plot elements to reduce
whitespace and improve the overall plot aesthetic.
(#549, #591 @grantmcdermott, @vincentarelbundock)
(#549, #591, #595 @grantmcdermott, @vincentarelbundock)

- Plot margins now correctly respond to missing and/or multi-line `main`,
`sub`, and `x`/`y` axis titles. For example, a plot without a `main` (or
Expand All @@ -48,6 +48,21 @@ visualizations.
directly (tick-to-label gap and label-to-title gap, respectively),
replacing the guesswork of manually combining `mar`, `mgp`, and `tcl`
values. (#590)
- The gap between y-axis tick labels and the y-axis title is now
constant regardless of label width (1-digit, 2-digit, decimals, etc.).
Previously the gap varied at the narrow/wide label boundary. (#596)
- New `gap.main` and `gap.sub` theme primitives control the spacing
between titles and the plot box. `gap.main` sets the gap from the main
title to whatever is below it (plot box or subtitle top); `gap.sub`
sets the gap from the subtitle to the plot box. Both default to `0.7`.
(#597)

- **Theme refinements**. The `"tufte"` and `"void"` themes are now dynamic
(responsive margins). The `"ipsum"` theme has been overhauled (bold title, no
ticks, fine grid, custom palette); the original variant is preserved as
`"ipsum2"`. The `"bw"` and `"classic"` themes now use smaller axis text and
tighter spacing to better match their ggplot2 counterparts.
(#595 @grantmcdermott)

### New features

Expand Down Expand Up @@ -75,11 +90,17 @@ visualizations.
providing finer control for spacing between ticks-labels and labels-titles,
respectively, in dynamic themes. See the **Dynamic themes** entry above.
(#590 @grantmcdermott)
- Similarly, `tinytheme()` also accepts `gap.main` and `gap.sub` primitives for
controlling the spacing between titles and the plot region.
(#595 @grantmcdermott)
- New `tinyplot(..., cap = <caption>)` argument for adding a caption to your
plots. Captions are drawn at the bottom of the plot and are best paired with
dynamic themes (since separation from `sub` is guaranteed). Appearance is
customizable via `tpar()` parameters: `adj.cap`, `cex.cap`, `col.cap`,
`font.cap`, and `line.cap`. (#592 @grantmcdermott)
- New themes: `"socviz"`, `"broadsheet"`, `"nber"`, and `"web"`, targeting
academic social science, newspaper, NBER working paper, and web-style (e.g.,
FiveThirtyEight) aesthetics respectively. (#595 @grantmcdermott)

### Bug fixes

Expand Down
22 changes: 10 additions & 12 deletions R/facet.R
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ draw_facet_window = function(
}
if (!is.null(yaxl)) yaxlabs = tinylabel(yaxlabs, yaxl)
# whtsbp = grconvertX(max(strwidth(yaxl, "figure")), from = "nfc", to = "lines") - 1
whtsbp = grconvertX(max(strwidth(yaxlabs, "figure", cex = par("cex.axis"))), from = "nfc", to = "lines") - grconvertX(0, from = "nfc", to = "lines") - 1
whtsbp = grconvertX(max(strwidth(yaxlabs, "figure", cex = par("cex.axis"))), from = "nfc", to = "lines") - grconvertX(0, from = "nfc", to = "lines") - 0.5
if (whtsbp > 0) {
omar = omar + c(0, whtsbp, 0, 0) * cex_fct_adj
fmar[2] = fmar[2] + whtsbp * cex_fct_adj
Expand All @@ -177,7 +177,7 @@ draw_facet_window = function(
if (!is.null(names(xlabs))) names(xlabs) else xlabs
}
if (!is.null(xaxl)) xaxlabs = tinylabel(xaxlabs, xaxl)
whtsbp = grconvertX(max(strwidth(xaxlabs, "figure", cex = par("cex.axis"))), from = "nfc", to = "lines") - 1
whtsbp = grconvertX(max(strwidth(xaxlabs, "figure", cex = par("cex.axis"))), from = "nfc", to = "lines") - 0.5
if (whtsbp > 0) {
omar = omar + c(whtsbp, 0, 0, 0) * cex_fct_adj
fmar[1] = fmar[1] + whtsbp * cex_fct_adj
Expand Down Expand Up @@ -239,25 +239,23 @@ draw_facet_window = function(
yaxlabs = if (!is.null(names(xlabs))) names(xlabs) else xlabs
} else {
# yaxl = axTicks(2)
yaxlabs = axisTicks(usr = extendrange(ylim, f = 0.04), log = par("ylog"))
ylim_usr = if (diff(ylim) == 0 && is.null(yaxb)) ylim + c(-0.5, 0.5) else extendrange(ylim, f = 0.04)
yaxlabs = axisTicks(usr = ylim_usr, log = par("ylog"))
}
if (!is.null(yaxl)) yaxlabs = tinylabel(yaxlabs, yaxl)
# whtsbp = grconvertX(max(strwidth(yaxlabs, "figure", cex = par("cex.axis"))), from = "nfc", to = "lines") - 1
whtsbp = grconvertX(max(strwidth(yaxlabs, "figure", cex = par("cex.axis"))), from = "nfc", to = "lines") - grconvertX(0, from = "nfc", to = "lines") - 1
if (whtsbp > 0) {
omar[2] = omar[2] + whtsbp
}
whtsbp = grconvertX(max(strwidth(yaxlabs, "figure", cex = par("cex.axis"))), from = "nfc", to = "lines") - grconvertX(0, from = "nfc", to = "lines") - 0.5
omar[2] = omar[2] + whtsbp
}
if (par("las") %in% 2:3) {
# extra whitespace bump on the x axis
# xaxl = axTicks(1)
xaxlabs = if (is.null(xlabs)) axisTicks(usr = extendrange(xlim, f = 0.04), log = par("xlog")) else
xlim_usr = if (diff(xlim) == 0 && is.null(xaxb)) xlim + c(-0.5, 0.5) else extendrange(xlim, f = 0.04)
xaxlabs = if (is.null(xlabs)) axisTicks(usr = xlim_usr, log = par("xlog")) else
if (!is.null(names(xlabs))) names(xlabs) else xlabs
if (!is.null(xaxl)) xaxlabs = tinylabel(xaxlabs, xaxl)
whtsbp = grconvertX(max(strwidth(xaxlabs, "figure", cex = par("cex.axis"))), from = "nfc", to = "lines") - 1
if (whtsbp > 0) {
omar[1] = omar[1] + whtsbp
}
whtsbp = grconvertX(max(strwidth(xaxlabs, "figure", cex = par("cex.axis"))), from = "nfc", to = "lines") - 0.5
omar[1] = omar[1] + whtsbp
}

par(mar = omar)
Expand Down
30 changes: 19 additions & 11 deletions R/tinyplot.R
Original file line number Diff line number Diff line change
Expand Up @@ -1031,7 +1031,7 @@ tinyplot.default = function(
get(paste0("theme_", .tinytheme), envir = asNamespace("tinyplot"))
} else NULL
.theme_mar = if (!is.null(.theme_def[["mar"]])) .theme_def[["mar"]] else par("mar")
.tpars = if (!is.null(.theme_def)) .theme_def else tpar()
.tpars = if (!is.null(.theme_def)) modifyList(.theme_def, tpar()) else tpar()
# Merge pending before.plot.new hook values into .tpars so user
# overrides passed via tinytheme(..., las = 2) (or tpar(...)) are
# visible to dynmar_side()/whtsbp before plot.new fires. Without this,
Expand Down Expand Up @@ -1094,6 +1094,8 @@ tinyplot.default = function(
# block runs before that. Pass .cex_axis to strwidth so measurements
# reflect the intended text size (par("cex.axis") isn't set yet either).
.whtsbp = c(0, 0, 0, 0)
.whtsbp_y_raw = 0
.whtsbp_x_raw = 0
.las = get_tpar("las", tpar_list = .tpars, default = par("las"))
if (.las %in% 1:2) {
if (type == "ridge") {
Expand All @@ -1103,27 +1105,33 @@ tinyplot.default = function(
} else if (type == "boxplot" && isTRUE(flip) && !is.null(xlabs)) {
yaxlabs = if (!is.null(names(xlabs))) names(xlabs) else xlabs
} else {
yaxlabs = axisTicks(usr = extendrange(ylim, f = 0.04), log = par("ylog"))
ylim_usr = if (diff(ylim) == 0 && is.null(yaxb)) ylim + c(-0.5, 0.5) else extendrange(ylim, f = 0.04)
yaxlabs = axisTicks(usr = ylim_usr, log = par("ylog"))
}
if (!is.null(yaxl)) yaxlabs = tinylabel(yaxlabs, yaxl)
whtsbp_y = grconvertX(max(strwidth(yaxlabs, "figure", cex = .cex_axis)), from = "nfc", to = "lines") -
grconvertX(0, from = "nfc", to = "lines") - 1
if (is.finite(whtsbp_y) && whtsbp_y > 0) .whtsbp[2] = whtsbp_y
.whtsbp_y_raw = grconvertX(max(strwidth(yaxlabs, "figure", cex = .cex_axis)), from = "nfc", to = "lines") -
grconvertX(0, from = "nfc", to = "lines") - 0.5
if (is.finite(.whtsbp_y_raw)) .whtsbp[2] = .whtsbp_y_raw
}
if (.las %in% 2:3) {
xaxlabs = if (is.null(xlabs)) axisTicks(usr = extendrange(xlim, f = 0.04), log = par("xlog")) else
xlim_usr = if (diff(xlim) == 0 && is.null(xaxb)) xlim + c(-0.5, 0.5) else extendrange(xlim, f = 0.04)
xaxlabs = if (is.null(xlabs)) axisTicks(usr = xlim_usr, log = par("xlog")) else
if (!is.null(names(xlabs))) names(xlabs) else xlabs
if (!is.null(xaxl)) xaxlabs = tinylabel(xaxlabs, xaxl)
whtsbp_x = grconvertX(max(strwidth(xaxlabs, "figure", cex = .cex_axis)), from = "nfc", to = "lines") - 1
if (is.finite(whtsbp_x) && whtsbp_x > 0) .whtsbp[1] = whtsbp_x
.whtsbp_x_raw = grconvertX(max(strwidth(xaxlabs, "figure", cex = .cex_axis)), from = "nfc", to = "lines") - 0.5
if (is.finite(.whtsbp_x_raw)) .whtsbp[1] = .whtsbp_x_raw
}

# Under facets, per-facet tick labels render smaller (scaled by
# cex_fct_adj), so whtsbp — which is computed from device font metrics
# — needs the same scaling to match the actual rendered margin used by
# draw_facet_window. Without this, draw_title's mar reserves too much
# space on the LHS and anchors the title too far right.
if (cex_fct_adj != 1) .whtsbp = .whtsbp * cex_fct_adj
if (cex_fct_adj != 1) {
.whtsbp = .whtsbp * cex_fct_adj
.whtsbp_y_raw = .whtsbp_y_raw * cex_fct_adj
.whtsbp_x_raw = .whtsbp_x_raw * cex_fct_adj
}

dynmar_computed = .theme_mar + .dyn
par(mar = dynmar_computed + .whtsbp)
Expand Down Expand Up @@ -1223,8 +1231,8 @@ tinyplot.default = function(
}

draw_title(main, sub, cap, xlab, ylab, legend, legend_args, opar,
xlab_line_offset = if (!is.null(dynmar_computed)) .whtsbp[1] else 0,
ylab_line_offset = if (!is.null(dynmar_computed)) .whtsbp[2] - .ymgp_shift - .ylab_cex_shift else 0)
xlab_line_offset = if (!is.null(dynmar_computed)) .whtsbp_x_raw else 0,
ylab_line_offset = if (!is.null(dynmar_computed)) .whtsbp_y_raw - max(0, .ymgp_shift) - .ylab_cex_shift else 0)
}


Expand Down
Loading
Loading