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
2 changes: 1 addition & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -63,4 +63,4 @@ Encoding: UTF-8
URL: https://grantmcdermott.com/tinyplot/
BugReports: https://github.com/grantmcdermott/tinyplot/issues
Roxygen: list(markdown = TRUE)
Config/roxygen2/version: 8.0.0
Config/roxygen2/version: 8.1.0
193 changes: 102 additions & 91 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export(type_ellipse)
export(type_errorbar)
export(type_function)
export(type_glm)
export(type_heatmap)
export(type_hexbin)
export(type_hist)
export(type_histogram)
Expand All @@ -51,97 +52,107 @@ export(type_spineplot)
export(type_spline)
export(type_summary)
export(type_text)
export(type_tile)
export(type_violin)
export(type_vline)
importFrom(grDevices,adjustcolor)
importFrom(grDevices,as.raster)
importFrom(grDevices,axisTicks)
importFrom(grDevices,cairo_pdf)
importFrom(grDevices,chull)
importFrom(grDevices,col2rgb)
importFrom(grDevices,colorRampPalette)
importFrom(grDevices,convertColor)
importFrom(grDevices,dev.cur)
importFrom(grDevices,dev.list)
importFrom(grDevices,dev.new)
importFrom(grDevices,dev.off)
importFrom(grDevices,extendrange)
importFrom(grDevices,gray.colors)
importFrom(grDevices,hcl)
importFrom(grDevices,hcl.colors)
importFrom(grDevices,hcl.pals)
importFrom(grDevices,jpeg)
importFrom(grDevices,nclass.Sturges)
importFrom(grDevices,palette)
importFrom(grDevices,palette.colors)
importFrom(grDevices,palette.pals)
importFrom(grDevices,pdf)
importFrom(grDevices,png)
importFrom(grDevices,recordGraphics)
importFrom(grDevices,svg)
importFrom(grDevices,xy.coords)
importFrom(graphics,Axis)
importFrom(graphics,abline)
importFrom(graphics,arrows)
importFrom(graphics,axTicks)
importFrom(graphics,axis)
importFrom(graphics,box)
importFrom(graphics,boxplot)
importFrom(graphics,grconvertX)
importFrom(graphics,grconvertY)
importFrom(graphics,hist)
importFrom(graphics,legend)
importFrom(graphics,lines)
importFrom(graphics,mtext)
importFrom(graphics,par)
importFrom(graphics,plot.default)
importFrom(graphics,plot.new)
importFrom(graphics,plot.window)
importFrom(graphics,points)
importFrom(graphics,polygon)
importFrom(graphics,polypath)
importFrom(graphics,rasterImage)
importFrom(graphics,rect)
importFrom(graphics,rug)
importFrom(graphics,segments)
importFrom(graphics,strheight)
importFrom(graphics,strwidth)
importFrom(graphics,text)
importFrom(graphics,title)
importFrom(graphics,xinch)
importFrom(stats,aggregate)
importFrom(stats,approx)
importFrom(stats,as.formula)
importFrom(stats,ave)
importFrom(stats,bw.SJ)
importFrom(stats,bw.bcv)
importFrom(stats,bw.nrd)
importFrom(stats,bw.nrd0)
importFrom(stats,bw.ucv)
importFrom(stats,cov)
importFrom(stats,density)
importFrom(stats,dnorm)
importFrom(stats,glm)
importFrom(stats,lm)
importFrom(stats,loess)
importFrom(stats,loess.control)
importFrom(stats,median)
importFrom(stats,model.frame)
importFrom(stats,na.omit)
importFrom(stats,ppoints)
importFrom(stats,predict)
importFrom(stats,qchisq)
importFrom(stats,qnorm)
importFrom(stats,qt)
importFrom(stats,quantile)
importFrom(stats,reformulate)
importFrom(stats,setNames)
importFrom(stats,spline)
importFrom(stats,terms)
importFrom(stats,time)
importFrom(stats,weighted.mean)
importFrom(grDevices,
adjustcolor,
as.raster,
axisTicks,
cairo_pdf,
chull,
col2rgb,
colorRampPalette,
convertColor,
dev.cur,
dev.list,
dev.new,
dev.off,
extendrange,
gray.colors,
hcl,
hcl.colors,
hcl.pals,
jpeg,
nclass.Sturges,
palette,
palette.colors,
palette.pals,
pdf,
png,
recordGraphics,
svg,
xy.coords
)
importFrom(graphics,
Axis,
abline,
arrows,
axTicks,
axis,
box,
boxplot,
grconvertX,
grconvertY,
hist,
legend,
lines,
mtext,
par,
plot.default,
plot.new,
plot.window,
points,
polygon,
polypath,
rasterImage,
rect,
rug,
segments,
strheight,
strwidth,
text,
title,
xinch
)
importFrom(stats,
aggregate,
approx,
as.formula,
ave,
bw.SJ,
bw.bcv,
bw.nrd,
bw.nrd0,
bw.ucv,
cov,
density,
dnorm,
glm,
lm,
loess,
loess.control,
median,
model.frame,
na.omit,
ppoints,
predict,
qchisq,
qnorm,
qt,
quantile,
reformulate,
sd,
setNames,
spline,
terms,
time,
weighted.mean
)
importFrom(tools,file_ext)
importFrom(utils,globalVariables)
importFrom(utils,head)
importFrom(utils,modifyList)
importFrom(utils,tail)
importFrom(utils,
globalVariables,
head,
modifyList,
tail
)
33 changes: 31 additions & 2 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,16 @@ where the formatting is also better._

#### New plot types

- `type_hexbin()` (equivalently, `type = "hexbin"`) for hexagonal bin plots, a
2D analogue of a histogram. (#667 @grantmcdermott)
- `type_hexbin()` / `"hexbin"` for hexagonal bin plots, a 2D analogue of a
histogram. (#667 @grantmcdermott)
- `type_tile()` / `"tile"` for tile plots, i.e. a grid of rectangles whose fill
encodes a third variable. (#677 @grantmcdermott)
- `type_heatmap()` / `"heatmap"` builds on `type_tile()`, adding a `scale`
argument that scales the fill values *within* each category of one axis. This
is analogous to base R's `heatmap()` function, and like the latter it z-scores
along the chosen margin by default. It also reverses the y-axis by default, so
that the first row sits at the top (again matching `heatmap()`); pass an
explicit `ylim` to override. (#677 @grantmcdermott)

#### Other new features

Expand Down Expand Up @@ -44,6 +52,17 @@ where the formatting is also better._
`"cat"` (console), in any combination; a destination the user has already
labelled is left alone. Shared bandwidths are reported once and named as
joint, individual bandwidths per group. (#287 @haomeng797-ship-it)
- New `cex.xaxs` and `cex.yaxs` graphical parameters allow the x- and y-axis
tick labels to be sized independently, e.g. `tpar(cex.yaxs = 0.6)` to shrink a
long list of category names on the y-axis without also shrinking the x-axis.
Both default to `NULL`, in which case the shared `cex.axis` value is used, so
existing plots are unaffected. (#677 @grantmcdermott)
- Themes:
- `"heatmap"` provides a dedicated companion theme to the new `type_tile()`
and `type_heatmap()` types (see above). The theme removes all axis padding,
so that tiles meet the panel edge, and also rotates the tick labels against
their respective axes. Colour fills default to the "tealgrn" sequential
palette. (#677 @grantmcdermott)

### Bug fixes

Expand All @@ -58,6 +77,16 @@ where the formatting is also better._
hand and so silently ignored `cex.axis`, `lwd.axis` and `lty.axis` (plus
their per-side variants), which was most visible under themes that set them,
e.g. `tinytheme("bw")`. (#673 @grantmcdermott)
- Axis tick labels now honour the themed `cex.axis` value. The internal axis call
passed it as `cex`, which base `axis()` ignores in favour of `cex.axis` when
sizing tick labels, so the setting had no effect on label size. This also means
the per-side `cex.xaxs`/`cex.yaxs` parameters (see above) take effect.
(#677 @grantmcdermott)
- Dynamic margins now measure each axis at its own tick-label size. The margin
and whitespace calculations read only the shared `cex.axis`, so a plot that
set `cex.xaxs`/`cex.yaxs` to different values clipped the labels on the larger
axis and reserved dead whitespace on the smaller one, e.g.
`tinytheme("heatmap", cex.xaxs = 2, cex.yaxs = 0.5)`. (#677 @grantmcdermott)
- Grouped and faceted plots no longer redraw axes once per empty group. This was
most visible for `"spineplot"` types (e.g. `facet = "by"`), where the
self-drawn axis labels were overplotted several times and rendered too heavy.
Expand Down
39 changes: 27 additions & 12 deletions R/facet.R
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,11 @@ draw_facet_window = function(
# Use that as the base instead of par("mar") which may have been
# reset by the before.plot.new hook.
side.sub = get_tpar("side.sub", tpar_list = tpars, default = 3)
# Tick labels are measured at their own side's cex, falling back to the
# shared par("cex.axis"). Measuring both sides at the shared value clips
# the wider axis and reserves dead space on the narrower one.
.cex_xaxs = get_tpar("cex.xaxs", tpar_list = tpars, default = par("cex.axis"))
.cex_yaxs = get_tpar("cex.yaxs", tpar_list = tpars, default = par("cex.axis"))
omar = dynmar_computed
omar[3] = dynmar_computed[3] + (1 + facet_newlines + 0.1) * facet_text
# Ensure fmar[3] doesn't exceed omar[3] - 0.1, which would make
Expand All @@ -169,15 +174,15 @@ draw_facet_window = function(
yaxlabs_all = lapply(yfree_split, function(yf) {
axisTicks(usr = extendrange(range(yf, na.rm = TRUE), f = 0.04), log = par("ylog"))
})
widths = vapply(yaxlabs_all, function(labs) max(strwidth(labs, "inches", cex = par("cex.axis"))), numeric(1L))
widths = vapply(yaxlabs_all, function(labs) max(strwidth(labs, "inches", cex = .cex_yaxs)), numeric(1L))
yaxlabs = yaxlabs_all[[which.max(widths)]]
} else {
yaxlabs = axisTicks(usr = extendrange(ylim, f = 0.04), log = par("ylog"))
}
}
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") - 0.5
whtsbp = grconvertX(max(strwidth(yaxlabs, "figure", cex = .cex_yaxs)), 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 @@ -200,14 +205,14 @@ draw_facet_window = function(
xaxlabs_all = lapply(xfree_split, function(xf) {
axisTicks(usr = extendrange(range(xf, na.rm = TRUE), f = 0.04), log = par("xlog"))
})
widths = vapply(xaxlabs_all, function(labs) max(strwidth(labs, "inches", cex = par("cex.axis"))), numeric(1L))
widths = vapply(xaxlabs_all, function(labs) max(strwidth(labs, "inches", cex = .cex_xaxs)), numeric(1L))
xaxlabs = xaxlabs_all[[which.max(widths)]]
} else {
xaxlabs = if (is.null(xlabs)) axisTicks(usr = extendrange(xlim, f = 0.04), 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") - 0.5
whtsbp = grconvertX(max(strwidth(xaxlabs, "figure", cex = .cex_xaxs)), 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 @@ -258,6 +263,9 @@ draw_facet_window = function(
# in tinyplot.default and passed via dynmar_computed; use them directly.
# Tick-label *width/height* (whtsbp) is added further below.
side.sub = get_tpar("side.sub", tpar_list = tpars, default = 3)
# Per-side tick-label cex; see the faceted branch above.
.cex_xaxs = get_tpar("cex.xaxs", tpar_list = tpars, default = par("cex.axis"))
.cex_yaxs = get_tpar("cex.yaxs", tpar_list = tpars, default = par("cex.axis"))
omar = dynmar_computed
# reserve RHS margin for types with a secondary axis (e.g. spineplot)
if (isTRUE(type_hints[["has_rhs_axis"]])) omar[4] = 2.1
Expand All @@ -271,8 +279,8 @@ draw_facet_window = function(
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") - 0.5
# whtsbp = grconvertX(max(strwidth(yaxlabs, "figure", cex = .cex_yaxs)), from = "nfc", to = "lines") - 1
whtsbp = grconvertX(max(strwidth(yaxlabs, "figure", cex = .cex_yaxs)), from = "nfc", to = "lines") - grconvertX(0, from = "nfc", to = "lines") - 0.5
omar[2] = omar[2] + whtsbp
}
if (par("las") %in% 2:3) {
Expand All @@ -282,7 +290,7 @@ draw_facet_window = function(
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") - 0.5
whtsbp = grconvertX(max(strwidth(xaxlabs, "figure", cex = .cex_xaxs)), from = "nfc", to = "lines") - 0.5
omar[1] = omar[1] + whtsbp
}

Expand Down Expand Up @@ -337,11 +345,15 @@ draw_facet_window = function(

# axes, frame.plot and grid
if (isTRUE(axes) || isTRUE(facet.args[["free"]])) {
# Note `cex.axis` rather than `cex`: base `axis()` sizes its tick labels
# from the former and silently ignores the latter, so passing `cex` here
# would leave `cex.xaxs`/`cex.yaxs` (and any theme that sets them) with no
# effect on label size.
args_x = list(x,
side = xside,
type = xaxt,
labeller = xaxl,
cex = get_tpar(c("cex.xaxs", "cex.axis"), 0.8, tpar_list = tpars),
cex.axis = get_tpar(c("cex.xaxs", "cex.axis"), 0.8, tpar_list = tpars),
lwd = get_tpar(c("lwd.xaxs", "lwd.axis"), 1, tpar_list = tpars),
lty = get_tpar(c("lty.xaxs", "lty.axis"), 1, tpar_list = tpars)
)
Expand All @@ -351,16 +363,19 @@ draw_facet_window = function(
side = yside,
type = yaxt,
labeller = yaxl,
cex = .ca,
cex.axis = .ca,
lwd = get_tpar(c("lwd.yaxs", "lwd.axis"), 1, tpar_list = tpars),
lty = get_tpar(c("lty.yaxs", "lty.axis"), 1, tpar_list = tpars)
)
if (!is.null(xaxb)) args_x$at = xaxb
if (!is.null(yaxb)) args_y$at = yaxb
# `xlabs` is only non-NULL when a type has placed categorical data on the
# x-axis, so its presence is the signal to draw labelled ticks.
# `xlabs`/`ylabs` are only non-NULL when a type has placed categorical data
# on that axis, so their presence is the signal to draw labelled ticks.
# The y-side previously listed the eligible types by name, but every type
# that populates `ylabs` does so precisely because it has categories to
# label, making the extra condition redundant (#665).
type_range_x = !is.null(xlabs)
type_range_y = !is.null(ylabs) && (type == "p" || (isTRUE(flip) && type %in% c("barplot", "pointrange", "errorbar", "ribbon", "boxplot", "violin")))
type_range_y = !is.null(ylabs)
if (type_range_x) {
args_x = modifyList(args_x, list(at = xlabs, labels = names(xlabs)))
}
Expand Down
Loading