Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
78 commits
Select commit Hold shift + click to select a range
cadf835
tinytheme
vincentarelbundock Nov 15, 2024
2a4271c
tinytheme: allow hook action to prepend, append, or replace
vincentarelbundock Nov 17, 2024
d958123
ipsum theme + new tpar arguments and refactor
vincentarelbundock Nov 17, 2024
31ddebb
docs
vincentarelbundock Nov 18, 2024
814cd35
main & sub can both be on top
vincentarelbundock Nov 18, 2024
5ce414c
theme_grey is terrible
vincentarelbundock Nov 18, 2024
e5278b2
tinytheme: better reset
vincentarelbundock Nov 18, 2024
a48de93
tinytheme() re-inits the content of .tpar
vincentarelbundock Nov 18, 2024
87ef098
refactor tinyAxis
vincentarelbundock Nov 19, 2024
29f8593
theme_dark
vincentarelbundock Nov 19, 2024
8949426
restore tinyAxis + drop theme_void
vincentarelbundock Nov 19, 2024
8acb3c7
xaxt is broken in themes
vincentarelbundock Nov 19, 2024
9734306
simplify tinytheme
vincentarelbundock Nov 19, 2024
d1f1b83
theme_ipsum
vincentarelbundock Nov 19, 2024
8401203
theme: - grey + ipsum
vincentarelbundock Nov 19, 2024
fb6e0a3
tests: re-generate snapshots
vincentarelbundock Nov 19, 2024
a64b458
vignette is broken
vincentarelbundock Nov 19, 2024
8ec6499
Merge branch 'main' into tinytheme
vincentarelbundock Nov 20, 2024
94a8509
tpar refactor
vincentarelbundock Nov 20, 2024
b782d6d
tpar refactor works
vincentarelbundock Nov 20, 2024
2b30329
still not working
vincentarelbundock Nov 20, 2024
95c5569
tests
vincentarelbundock Nov 20, 2024
3f47e0d
theme_dark fix
vincentarelbundock Nov 20, 2024
d767af0
tpar(): hook and init arguments
vincentarelbundock Nov 20, 2024
5337cae
vignettes
vincentarelbundock Nov 20, 2024
82107c0
pkgload -> library()
vincentarelbundock Nov 20, 2024
de30af6
drop useless param doc
vincentarelbundock Nov 20, 2024
b5bacb3
drop usless docs
vincentarelbundock Nov 20, 2024
b23ba5e
vignette header levels
vincentarelbundock Nov 20, 2024
2ad92d6
themes vignette
vincentarelbundock Nov 20, 2024
ef3af55
reset themes in every test file
vincentarelbundock Nov 20, 2024
bb6f5e5
custom themes
vincentarelbundock Nov 20, 2024
bc39899
typo
vincentarelbundock Nov 20, 2024
d032ca2
theme quarto settings
vincentarelbundock Nov 20, 2024
f3b2cd5
tinytheme: add "default" + deprecate "custom"
vincentarelbundock Nov 21, 2024
443568a
tinytheme supports palette
vincentarelbundock Nov 21, 2024
a369432
dark theme lighter grid
vincentarelbundock Nov 21, 2024
5634b4d
theme_default
vincentarelbundock Nov 21, 2024
50a36d7
docs
vincentarelbundock Nov 21, 2024
fd19483
shutdown graphical devices
vincentarelbundock Nov 21, 2024
7d182d3
quarto bug
vincentarelbundock Nov 21, 2024
913e495
tinytheme: palette.sequential + palette.qualitative
vincentarelbundock Nov 21, 2024
1c3477c
default palettes in new themes: okabe-ito and mako
vincentarelbundock Nov 21, 2024
b4275e1
Mako -> Ag-Sunset
vincentarelbundock Nov 22, 2024
b8b6119
Merge branch 'main' into tinytheme
vincentarelbundock Nov 26, 2024
79e07ef
new snapshots
vincentarelbundock Nov 26, 2024
8f3fb8a
man
vincentarelbundock Nov 26, 2024
e058ad5
importFrom approx (ridge problem)
vincentarelbundock Nov 26, 2024
87ed77c
docs
vincentarelbundock Nov 26, 2024
04dd1d6
restore par() first initialized
vincentarelbundock Nov 26, 2024
5d6c80e
docs
vincentarelbundock Nov 26, 2024
a10e4c8
tweak titles and axes, revert tests
grantmcdermott Nov 26, 2024
79a5629
Merge branch 'tinytheme' of https://github.com/vincentarelbundock/tin…
grantmcdermott Nov 26, 2024
1227b7b
snapshot update
grantmcdermott Nov 26, 2024
6d945fe
Merge branch 'main' into tinytheme
vincentarelbundock Nov 27, 2024
12c81cd
quarto: man/tinytheme.qmd
vincentarelbundock Nov 27, 2024
ca60353
vignettes: code review
vincentarelbundock Nov 27, 2024
4a6c2b7
tinyAxis comment
vincentarelbundock Nov 27, 2024
6484e02
allow qualitative palettes with more than 8 levels
vincentarelbundock Nov 27, 2024
244a60a
hotfix qualitative palette
vincentarelbundock Nov 27, 2024
06c2d1e
refactor theme logic
grantmcdermott Dec 2, 2024
94af6ae
plot.new catch for par("bg") theme change
grantmcdermott Dec 2, 2024
8b148ec
simplify test
grantmcdermott Dec 2, 2024
0655962
docs
grantmcdermott Dec 2, 2024
dccc3b0
Regular main font for several themes
grantmcdermott Dec 2, 2024
9d1380b
dynamic margins
grantmcdermott Dec 9, 2024
347a2f7
docs
grantmcdermott Dec 10, 2024
9823257
tests
grantmcdermott Dec 10, 2024
54ca0e2
tweak examples
grantmcdermott Dec 10, 2024
5f7f5ac
support left = "left!"
grantmcdermott Dec 10, 2024
a4a232a
typo
grantmcdermott Dec 10, 2024
70a5282
support legend = "bottom!"
grantmcdermott Dec 10, 2024
2cf096f
more tests
grantmcdermott Dec 10, 2024
13cd30c
namespace and docs
grantmcdermott Dec 10, 2024
1ee6db5
add family and redoc
grantmcdermott Dec 10, 2024
9a3c3ba
Update vignettes
grantmcdermott Dec 11, 2024
1d2911f
docs
grantmcdermott Dec 11, 2024
38d4b66
document dynmar
grantmcdermott Dec 11, 2024
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: 2 additions & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export(plt)
export(plt_add)
export(tinyplot)
export(tinyplot_add)
export(tinytheme)
export(tpar)
export(type_abline)
export(type_area)
Expand Down Expand Up @@ -37,6 +38,7 @@ export(type_spline)
export(type_vline)
importFrom(grDevices,adjustcolor)
importFrom(grDevices,as.raster)
importFrom(grDevices,axisTicks)
importFrom(grDevices,col2rgb)
importFrom(grDevices,colorRampPalette)
importFrom(grDevices,convertColor)
Expand Down
9 changes: 9 additions & 0 deletions R/by_aesthetics.R
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,15 @@ by_col = function(ngrps = 1L, col = NULL, palette = NULL, gradient = NULL, order
ngrps = 100L
}

if (is.null(palette)) {
pal_qual = get_tpar("palette.qualitative", default = NULL)
if (ngrps <= max(c(length(pal_qual), 8))) {
palette = pal_qual
} else {
palette = get_tpar("palette.sequential", default = NULL)
}
}

# palette = substitute(palette, env = parent.env(environment()))

# special "by" convenience keyword (will treat as NULL & handle grouping below)
Expand Down
59 changes: 50 additions & 9 deletions R/draw_legend.R
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,8 @@ draw_legend = function(

## restore inner margin defaults
## (in case the plot region/margins were affected by the preceding tinyplot call)
if (any(ooma != 0)) {
dynmar = isTRUE(.tpar[["dynmar"]])
if (any(ooma != 0) && !dynmar) {
if ( ooma[1] != 0 & omar[1] == par("mgp")[1] + 1*par("cex.lab") ) omar[1] = 5.1
if ( ooma[2] != 0 & omar[2] == par("mgp")[1] + 1*par("cex.lab") ) omar[2] = 4.1
if ( ooma[3] == topmar_epsilon & omar[3] != 4.1 ) omar[3] = 4.1
Expand All @@ -219,7 +220,6 @@ draw_legend = function(
par(omd = c(0,1,0,1))
ooma = par("oma")


## Legend to outer side (either right or left) of plot
if (grepl("right!$|left!$", legend_args[["x"]])) {

Expand All @@ -244,7 +244,22 @@ draw_legend = function(
}
par(mar = omar)

if (isTRUE(new_plot)) plot.new()
# if (isTRUE(new_plot)) plot.new()
if (isTRUE(new_plot)) {
plot.new()
# Experimental: For themed + dynamic plots, we need to make sure the
# adjusted plot margins for the legend are reinstated (after being
# overwritten by the before.plot.new hook.
if (dynmar) {
omar = par("mar")
if (outer_right) {
omar[4] = 0
} else {
omar[2] = par("mgp")[1] + 1*par("cex.lab")
}
par(mar = omar)
}
}

legend_args[["horiz"]] = FALSE

Expand Down Expand Up @@ -290,9 +305,13 @@ draw_legend = function(
# GM: The legend inset spacing only works _exactly_ if we refresh the plot
# area. I'm not sure why (and it works properly if we use the same
# parameters manually while debugging), but this hack seems to work.
par(new = TRUE)
## v0.3.0 update: Using (temporary) hook instead of direct par(new = TRUE)
## assignment to play nice with tinytheme logic.
oldhook = getHook("before.plot.new")
setHook("before.plot.new", function() par(new = TRUE), action = "append")
setHook("before.plot.new", function() par(mar = omar), action = "append")
plot.new()
par(new = FALSE)
setHook("before.plot.new", oldhook, action = "replace")
# Finally, set the inset as part of the legend args.
legend_args[["inset"]] = c(1+inset, 0)

Expand All @@ -310,7 +329,7 @@ draw_legend = function(
## width---will be off the first time.
if (outer_bottom) {
omar[1] = par("mgp")[1] + 1*par("cex.lab")
if (isTRUE(has_sub)) omar[1] = omar[1] + 1*par("cex.sub")
if (isTRUE(has_sub) && (is.null(.tpar[["side.sub"]]) || .tpar[["side.sub"]]==1)) omar[1] = omar[1] + 1*par("cex.sub")
} else {
## For "top!", the logic is slightly different: We don't expand the outer
## margin b/c we need the legend to come underneath the main title. So
Expand All @@ -320,7 +339,25 @@ draw_legend = function(
}
par(mar = omar)

if (isTRUE(new_plot)) plot.new()
# if (isTRUE(new_plot)) plot.new()
if (isTRUE(new_plot)) {
plot.new()
# Experimental: For themed + dynamic plots, we need to make sure the
# adjusted plot margins for the legend are reinstated (after being
# overwritten by the before.plot.new hook.
if (dynmar) {
omar = par("mar")
if (outer_bottom) {
# omar[1] = par("mgp")[1] + 1*par("cex.lab")
omar[1] = theme_clean$mgp[1] + 1*par("cex.lab") ## bit of a hack
if (isTRUE(has_sub) && (is.null(.tpar[["side.sub"]]) || .tpar[["side.sub"]]==1)) omar[1] = omar[1] + 1*par("cex.sub")
} else {
ooma[3] = ooma[3] + topmar_epsilon
par(oma = ooma)
}
par(mar = omar)
}
}

legend_args[["horiz"]] = TRUE

Expand Down Expand Up @@ -381,9 +418,13 @@ draw_legend = function(
# GM: The legend inset spacing only works _exactly_ if we refresh the plot
# area. I'm not sure why (and it works properly if we use the same
# parameters manually while debugging), but this hack seems to work.
par(new = TRUE)
## v0.3.0 update: Using (temporary) hook instead of direct par(new = TRUE)
## assignment to play nice with tinytheme logic.
oldhook = getHook("before.plot.new")
setHook("before.plot.new", function() par(new = TRUE), action = "append")
setHook("before.plot.new", function() par(mar = omar), action = "append") ## experimental dynmar
plot.new()
par(new = FALSE)
setHook("before.plot.new", oldhook, action = "replace")
# Finally, set the inset as part of the legend args.
legend_args[["inset"]] = c(0, 1+inset)

Expand Down
150 changes: 108 additions & 42 deletions R/facet.R
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Facet layout structure
#
# This function is called by `tinyplot`. Given some inputs, it returns
# This function is called by `tinyplot`. Given some inputs, it returns
# information about the layout of the facets.
#
facet_layout = function(facet, add = FALSE, facet.args = list()) {
Expand Down Expand Up @@ -49,7 +49,7 @@ facet_layout = function(facet, add = FALSE, facet.args = list()) {
facets = ifacet = nfacets = oxaxis = oyaxis = 1
cex_fct_adj = 1
}

list(
facets = facets,
ifacet = ifacet,
Expand Down Expand Up @@ -119,11 +119,19 @@ get_facet_fml = function(formula, data = NULL) {
# internal function to draw window with different facets, grids, axes, etc.

draw_facet_window = function(grid, ...) {

list2env(list(...), environment())

if (isFALSE(add)) {
# draw background color only in the grid rectangle
grid.bg = get_tpar("grid.bg")
if (!is.null(grid.bg)) {
corners = par("usr")
rect(corners[1], corners[3], corners[2], corners[4], col = grid.bg, border = NA)
}

## dynamic margins flag
dynmar = isTRUE(.tpar[["dynmar"]])

if (isFALSE(add)) {
## optionally allow to modify the style of axis interval calculation
if (!is.null(xaxs)) par(xaxs = xaxs)
if (!is.null(yaxs)) par(yaxs = yaxs)
Expand Down Expand Up @@ -168,6 +176,41 @@ draw_facet_window = function(grid, ...) {
fmar[3] = fmar[3] + facet_newlines * facet_text / cex_fct_adj

omar = par("mar")

## Dynamic plot margin adjustments
if (dynmar) {
if (par("las") %in% 1:2) {
# extra whitespace bump on the y axis
# yaxl = axTicks(2)
yaxl = axisTicks(usr = extendrange(ylim, f = 0.04), log = par("ylog"))
# whtsbp = grconvertX(max(strwidth(yaxl, "figure")), from = "nfc", to = "lines") - 1
whtsbp = grconvertX(max(strwidth(yaxl, "figure")), from = "nfc", to = "lines") - grconvertX(0, from = "nfc", to = "lines") - 1
if (whtsbp > 0) {
omar = omar + c(0, whtsbp, 0, 0) * cex_fct_adj
fmar[2] = fmar[2] + whtsbp * cex_fct_adj
}
}
if (par("las") %in% 2:3) {
# extra whitespace bump on the x axis
# xaxl = axTicks(1)
xaxl = axisTicks(usr = extendrange(xlim, f = 0.04), log = par("xlog"))
whtsbp = grconvertY(max(strwidth(xaxl, "figure")), from = "nfc", to = "lines") - 1
# whtsbp = grconvertY(max(strwidth(xaxl, "figure")), from = "nfc", to = "lines") - grconvertY(0, from = "nfc", to = "lines") - 1
if (whtsbp > 0) {
omar = omar + c(whtsbp, 0, 0, 0) * cex_fct_adj
fmar[1] = fmar[1] + whtsbp * cex_fct_adj
}
}
# FIXME: Is this causing issues for lhs legends with facet_grid?
# catch for missing rhs legend
if (isTRUE(attr(facet, "facet_grid")) && !has_legend) {
omar[4] = omar[4] + 1
}
# Extra reduction if no plot frame to reduce whitespace
if (isFALSE(frame.plot) && !isTRUE(facet.args[["free"]])) {
fmar[2] = fmar[2] - (whtsbp * cex_fct_adj)
}
}

# Now we set the margins. The trick here is that we simultaneously adjust
# inner (mar) and outer (oma) margins by the same amount, but in opposite
Expand All @@ -189,6 +232,31 @@ draw_facet_window = function(grid, ...) {
# Now that the margins have been set, arrange facet rows and columns based
# on our earlier calculations.
par(mfrow = c(nfacet_rows, nfacet_cols))
} else if (dynmar) {
# Dynamic plot margin adjustments
omar = par("mar")
omar = omar - c(0, 0, 1, 0) # reduce top whitespace since no facet (title)
if (par("las") %in% 1:2) {
# extra whitespace bump on the y axis
# yaxl = axTicks(2)
yaxl = axisTicks(usr = extendrange(ylim, f = 0.04), log = par("ylog"))
# whtsbp = grconvertX(max(strwidth(yaxl, "figure")), from = "nfc", to = "lines") - 1
whtsbp = grconvertX(max(strwidth(yaxl, "figure")), from = "nfc", to = "lines") - grconvertX(0, from = "nfc", to = "lines") - 1
if (whtsbp > 0) {
omar[2] = omar[2] + whtsbp
}
}
if (par("las") %in% 2:3) {
# extra whitespace bump on the x axis
# xaxl = axTicks(1)
xaxl = axisTicks(usr = extendrange(ylim, f = 0.04), log = par("xlog"))
whtsbp = grconvertY(max(strwidth(xaxl, "figure")), from = "nfc", to = "lines") - 1
# whtsbp = grconvertY(max(strwidth(xaxl, "figure")), from = "nfc", to = "lines") - grconvertY(0, from = "nfc", to = "lines") - 1
if (whtsbp > 0) {
omar[1] = omar[1] + whtsbp
}
}
par(mar = omar)
}

## Loop over the individual facet windows and draw the plot region
Expand Down Expand Up @@ -236,10 +304,32 @@ draw_facet_window = function(grid, ...) {
yside = 2
}


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

args_x = list(x,
side = xside,
type = xaxt,
cex = get_tpar(c("cex.xaxs", "cex.axis"), 0.8),
lwd = get_tpar(c("lwd.xaxs", "lwd.axis"), 1),
lty = get_tpar(c("lty.xaxs", "lty.axis"), 1)
)
args_y = list(y,
side = yside,
type = yaxt,
cex = get_tpar(c("cex.yaxs", "cex.axis"), 0.8),
lwd = get_tpar(c("lwd.yaxs", "lwd.axis"), 1),
lty = get_tpar(c("lty.yaxs", "lty.axis"), 1)
)
type_range_x = type %in% c("pointrange", "errorbar", "ribbon", "boxplot", "p") && !is.null(xlabs)
type_range_y = isTRUE(flip) && type %in% c("pointrange", "errorbar", "ribbon", "boxplot", "p") && !is.null(ylabs)
if (type_range_x) {
args_x = modifyList(args_x, list(at = xlabs, labels = names(xlabs)))
}
if (type_range_y) {
args_y = modifyList(args_y, list(at = ylabs, labels = names(ylabs)))
}

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.",
Expand All @@ -248,20 +338,20 @@ draw_facet_window = function(grid, ...) {
)
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)
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) {
if (ii == 1) {
fusr = replicate(4, vector("double", length = nfacets), simplify = FALSE)
assign(".fusr", fusr, envir = get(".tinyplot_env", envir = parent.env(environment())))
}
Expand All @@ -281,38 +371,16 @@ draw_facet_window = function(grid, ...) {
} else {
tinyAxis(yfree, side = yside, type = yaxt)
}
# For fixed facets we can just reuse the same plot extent and axes limits

# 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)
} else {
tinyAxis(x, side = xside, type = xaxt)
}
# tinyAxis(y, side = yside, type = yaxt)
if (isTRUE(flip) && type %in% c("pointrange", "errorbar", "ribbon", "boxplot", "p") && !is.null(ylabs)) {
tinyAxis(y, side = yside, at = ylabs, labels = names(ylabs), type = yaxt)
} else {
tinyAxis(y, side = yside, type = yaxt)
}
do.call(tinyAxis, args_x)
do.call(tinyAxis, args_y)
Comment thread
vincentarelbundock marked this conversation as resolved.
} else {
# ... else only print the "outside" axes.
if (ii %in% oxaxis) {
if (type %in% c("pointrange", "errorbar", "ribbon", "boxplot", "p") && !is.null(xlabs)) {
tinyAxis(x, side = xside, at = xlabs, labels = names(xlabs), type = xaxt)
} else {
tinyAxis(x, side = xside, type = xaxt)
}
}
if (ii %in% oyaxis) {
# tinyAxis(y, side = yside, type = yaxt)
if (isTRUE(flip) && type %in% c("pointrange", "errorbar", "ribbon", "boxplot", "p") && !is.null(ylabs)) {
tinyAxis(y, side = yside, at = ylabs, labels = names(ylabs), type = yaxt)
} else {
tinyAxis(y, side = yside, type = yaxt)
}
}
if (ii %in% oxaxis) do.call(tinyAxis, args_x)
if (ii %in% oyaxis) do.call(tinyAxis, args_y)
}
}

Expand Down Expand Up @@ -380,7 +448,6 @@ draw_facet_window = function(grid, ...) {
if (xlog) {
line_height = grconvertX(line_height, from = "lines", to = "user") / grconvertX(0, from = "lines", to = "user")
rect_width = corners[2] * line_height

} else {
line_height = grconvertX(line_height, from = "lines", to = "user") - grconvertX(0, from = "lines", to = "user")
rect_width = corners[2] + line_height
Expand All @@ -394,7 +461,6 @@ draw_facet_window = function(grid, ...) {
if (xlog) {
xpos = grconvertX(0.4, from = "lines", to = "user") / grconvertX(0, from = "lines", to = "user")
xpos = corners[2] * xpos

} else {
xpos = grconvertX(0.4, from = "lines", to = "user") - grconvertX(0, from = "lines", to = "user")
xpos = corners[2] + xpos
Expand Down Expand Up @@ -498,10 +564,9 @@ draw_facet_window = function(grid, ...) {
grid
}
}

# drawn elements
if (!is.null(draw)) eval(draw)

} # end of ii facet loop
} # end of add check

Expand All @@ -519,5 +584,6 @@ is_facet_position = function(position, ifacet, facet_window_args) {
"right" = ifacet %in% pmin(ni, seq(1L, ni, by = nc) + nc - 1L),
"top" = ifacet %in% head(id, nc),
"bottom" = ifacet %in% tail(id, nc),
NA)
NA
)
}
Loading