Skip to content
51 changes: 48 additions & 3 deletions R/facet.R
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ draw_facet_window = function(grid, ...) {
if (!(nfacet_rows == 2 && nfacet_cols == 2)) fmar = fmar * .75
}
# Extra reduction if no plot frame to reduce whitespace
if (isFALSE(frame.plot)) {
if (isFALSE(frame.plot) && !isTRUE(facet.args[["free"]])) {
fmar = fmar - 0.5
}

Expand Down Expand Up @@ -236,9 +236,54 @@ draw_facet_window = function(grid, ...) {
yside = 2
}


# axes, frame.plot and grid
if (isTRUE(axes)) {
if (isTRUE(frame.plot)) {
if (isTRUE(axes) || isTRUE(facet.args[["free"]])) {

if (isTRUE(facet.args[["free"]]) && (par("xlog") || par("ylog"))) {
warning(
"\nFree scale axes for faceted plots are currently not supported if the axes are logged. Reverting back to fixed scales.",
"\nIf support for this feature is important to you, please raise an issue on our GitHub repo:",
"\nhttps://github.com/grantmcdermott/tinyplot/issues\n"
)
facet.args[["free"]] = FALSE
}

# Special logic if facets are free...
if (isTRUE(facet.args[["free"]])) {
# First, we need to calculate the plot extent and axes range of each
# individual facet.
xfree = split(c(x, xmin, xmax), facet)[[ii]]
yfree = split(c(y, ymin, ymax), facet)[[ii]]
xlim = range(xfree, na.rm = TRUE)
ylim = range(yfree, na.rm = TRUE)
xext = extendrange(xlim, f = 0.04)
yext = extendrange(ylim, f = 0.04)
# We'll save this in a special .fusr env var (list) that we'll re-use
# when it comes to plotting the actual elements later
if (ii==1) {
fusr = replicate(4, vector("double", length = nfacets), simplify = FALSE)
assign(".fusr", fusr, envir = get(".tinyplot_env", envir = parent.env(environment())))
}
fusr = get(".fusr", envir = get(".tinyplot_env", envir = parent.env(environment())))
fusr[[ii]] = c(xext, yext)
assign(".fusr", fusr, envir = get(".tinyplot_env", envir = parent.env(environment())))
# Explicitly set (override) the current facet extent
par(usr = fusr[[ii]])
# if plot frame is true then print axes per normal...
if (type %in% c("pointrange", "errorbar", "ribbon", "boxplot", "p") && !is.null(xlabs)) {
tinyAxis(xfree, side = xside, at = xlabs, labels = names(xlabs), type = xaxt)
} else {
tinyAxis(xfree, side = xside, type = xaxt)
}
if (isTRUE(flip) && type %in% c("pointrange", "errorbar", "ribbon", "boxplot", "p") && !is.null(ylabs)) {
tinyAxis(yfree, side = yside, at = ylabs, labels = names(ylabs), type = yaxt)
} else {
tinyAxis(yfree, side = yside, type = yaxt)
}

# For fixed facets we can just reuse the same plot extent and axes limits
} else if (isTRUE(frame.plot)) {
# if plot frame is true then print axes per normal...
if (type %in% c("pointrange", "errorbar", "ribbon", "boxplot", "p") && !is.null(xlabs)) {
tinyAxis(x, side = xside, at = xlabs, labels = names(xlabs), type = xaxt)
Expand Down
13 changes: 13 additions & 0 deletions R/tinyplot.R
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@
#' precedence if both are specified together. Ignored if a two-sided formula
#' is passed to the main `facet` argument, since the layout is arranged in a
#' fixed grid.
#' - `free` a logical value indicating whether the axis limits (scales) for
#' each individual facet should adjust independently to match the range of
#' the data within that facet. Default is `FALSE`. Separate free scaling of
#' the x- or y-axis (i.e., whilst holding the other axis fixed) is not
#' currently supported.
#' - `fmar` a vector of form `c(b,l,t,r)` for controlling the base margin
#' between facets in terms of lines. Defaults to the value of `tpar("fmar")`,
#' which should be `c(1,1,1,1)`, i.e. a single line of padding around each
Expand Down Expand Up @@ -1096,6 +1101,14 @@ tinyplot.default = function(
mfgj = ii %% nfacet_cols
if (mfgj == 0) mfgj = nfacet_cols
par(mfg = c(mfgi, mfgj))

# For free facets, we need to reset par(usr) based extent of that
# particular facet... which we calculated and saved to the .fusr env var
# (list) back in draw_facet_window()
if (isTRUE(facet.args[["free"]])) {
fusr = get(".fusr", envir = get(".tinyplot_env", envir = parent.env(environment())))
par(usr = fusr[[ii]])
}
}

# empty plot flag
Expand Down
Loading