diff --git a/NEWS.md b/NEWS.md index 3abb02c650..deea3e6a7e 100644 --- a/NEWS.md +++ b/NEWS.md @@ -121,6 +121,11 @@ core developer team. * `sec_axis()` now accepts functions as well as formulas (@yutannihilation, #3031). +* New theme elements allowing different ticks lengths for each axis. For instance, + this can be used to have inwards ticks on the x-axis (`axis.ticks.length.x`) and + outwards ticks on the y-axis (`axis.ticks.length.y`) (@pank, #2935). + + # ggplot2 3.1.0 ## Breaking changes @@ -230,7 +235,6 @@ This is a minor release and breaking changes have been kept to a minimum. End us `nlevel`, an alias for `scaled`, to better match the syntax of `stat_bin()` (@bjreisman, #2679). - # ggplot2 3.0.0 ## Breaking changes diff --git a/R/guides-axis.r b/R/guides-axis.r index 836c11eee6..52eefea69d 100644 --- a/R/guides-axis.r +++ b/R/guides-axis.r @@ -14,6 +14,31 @@ guide_axis <- function(at, labels, position = "right", theme) { zero <- unit(0, "npc") one <- unit(1, "npc") + theme$axis.ticks.length.x.bottom <- with( + theme, + axis.ticks.length.x.bottom %||% + axis.ticks.length.x %||% + axis.ticks.length + ) + theme$axis.ticks.length.x.top <- with( + theme, + axis.ticks.length.x.top %||% + axis.ticks.length.x %||% + axis.ticks.length + ) + theme$axis.ticks.length.y.left <- with( + theme, + axis.ticks.length.y.left %||% + axis.ticks.length.y %||% + axis.ticks.length + ) + theme$axis.ticks.length.y.right <- with( + theme, + axis.ticks.length.y.right %||% + axis.ticks.length.y %||% + axis.ticks.length + ) + label_render <- switch(position, top = "axis.text.x.top", bottom = "axis.text.x.bottom", left = "axis.text.y.left", right = "axis.text.y.right" @@ -22,12 +47,12 @@ guide_axis <- function(at, labels, position = "right", theme) { label_x <- switch(position, top = , bottom = at, - right = theme$axis.ticks.length, - left = one - theme$axis.ticks.length + right = theme$axis.ticks.length.y.right, + left = one - theme$axis.ticks.length.y.left ) label_y <- switch(position, - top = theme$axis.ticks.length, - bottom = one - theme$axis.ticks.length, + top = theme$axis.ticks.length.x.top, + bottom = one - theme$axis.ticks.length.x.bottom, right = , left = at ) @@ -58,18 +83,18 @@ guide_axis <- function(at, labels, position = "right", theme) { ticks <- switch(position, top = element_render(theme, "axis.ticks.x.top", x = rep(at, each = 2), - y = rep(unit.c(zero, theme$axis.ticks.length), nticks), + y = rep(unit.c(zero, theme$axis.ticks.length.x.top), nticks), id.lengths = rep(2, nticks)), bottom = element_render(theme, "axis.ticks.x.bottom", x = rep(at, each = 2), - y = rep(unit.c(one - theme$axis.ticks.length, one), nticks), + y = rep(unit.c(one - theme$axis.ticks.length.x.bottom, one), nticks), id.lengths = rep(2, nticks)), right = element_render(theme, "axis.ticks.y.right", - x = rep(unit.c(zero, theme$axis.ticks.length), nticks), + x = rep(unit.c(zero, theme$axis.ticks.length.y.right), nticks), y = rep(at, each = 2), id.lengths = rep(2, nticks)), left = element_render(theme, "axis.ticks.y.left", - x = rep(unit.c(one - theme$axis.ticks.length, one), nticks), + x = rep(unit.c(one - theme$axis.ticks.length.y.left, one), nticks), y = rep(at, each = 2), id.lengths = rep(2, nticks)) ) @@ -79,21 +104,21 @@ guide_axis <- function(at, labels, position = "right", theme) { top = gtable_col("axis", grobs = list(labels, ticks), width = one, - heights = unit.c(grobHeight(labels), theme$axis.ticks.length) + heights = unit.c(grobHeight(labels), theme$axis.ticks.length.x.top) ), bottom = gtable_col("axis", grobs = list(ticks, labels), width = one, - heights = unit.c(theme$axis.ticks.length, grobHeight(labels)) + heights = unit.c(theme$axis.ticks.length.x.bottom, grobHeight(labels)) ), right = gtable_row("axis", grobs = list(ticks, labels), - widths = unit.c(theme$axis.ticks.length, grobWidth(labels)), + widths = unit.c(theme$axis.ticks.length.y.right, grobWidth(labels)), height = one ), left = gtable_row("axis", grobs = list(labels, ticks), - widths = unit.c(grobWidth(labels), theme$axis.ticks.length), + widths = unit.c(grobWidth(labels), theme$axis.ticks.length.y.left), height = one ) ) diff --git a/R/theme-defaults.r b/R/theme-defaults.r index 7719744360..876b5787e0 100644 --- a/R/theme-defaults.r +++ b/R/theme-defaults.r @@ -139,6 +139,12 @@ theme_grey <- function(base_size = 11, base_family = "", axis.text.y.right = element_text(margin = margin(l = 0.8 * half_line / 2), hjust = 0), axis.ticks = element_line(colour = "grey20"), axis.ticks.length = unit(half_line / 2, "pt"), + axis.ticks.length.x = NULL, + axis.ticks.length.x.top = NULL, + axis.ticks.length.x.bottom = NULL, + axis.ticks.length.y = NULL, + axis.ticks.length.y.left = NULL, + axis.ticks.length.y.right = NULL, axis.title.x = element_text( margin = margin(t = half_line / 2), vjust = 1 @@ -459,6 +465,12 @@ theme_void <- function(base_size = 11, base_family = "", axis.text = element_blank(), axis.title = element_blank(), axis.ticks.length = unit(0, "pt"), + axis.ticks.length.x = NULL, + axis.ticks.length.x.top = NULL, + axis.ticks.length.x.bottom = NULL, + axis.ticks.length.y = NULL, + axis.ticks.length.y.left = NULL, + axis.ticks.length.y.right = NULL, legend.box = NULL, legend.key.size = unit(1.2, "lines"), legend.position = "right", @@ -528,6 +540,12 @@ theme_test <- function(base_size = 11, base_family = "", axis.text.y.right = element_text(margin = margin(l = 0.8 * half_line / 2), hjust = 0), axis.ticks = element_line(colour = "grey20"), axis.ticks.length = unit(half_line / 2, "pt"), + axis.ticks.length.x = NULL, + axis.ticks.length.x.top = NULL, + axis.ticks.length.x.bottom = NULL, + axis.ticks.length.y = NULL, + axis.ticks.length.y.left = NULL, + axis.ticks.length.y.right = NULL, axis.title.x = element_text( margin = margin(t = half_line / 2), vjust = 1 diff --git a/R/theme-elements.r b/R/theme-elements.r index b343af54bd..247c4c868c 100644 --- a/R/theme-elements.r +++ b/R/theme-elements.r @@ -292,6 +292,12 @@ el_def <- function(class = NULL, inherit = NULL, description = NULL) { axis.text.y.left = el_def("element_text", "axis.text.y"), axis.text.y.right = el_def("element_text", "axis.text.y"), axis.ticks.length = el_def("unit"), + axis.ticks.length.x = el_def("unit", "axis.ticks.length"), + axis.ticks.length.x.top = el_def("unit", "axis.ticks.length.x"), + axis.ticks.length.x.bottom = el_def("unit", "axis.ticks.length.x"), + axis.ticks.length.y = el_def("unit", "axis.ticks.length"), + axis.ticks.length.y.left = el_def("unit", "axis.ticks.length.y"), + axis.ticks.length.y.right = el_def("unit", "axis.ticks.length.y"), axis.ticks.x = el_def("element_line", "axis.ticks"), axis.ticks.x.top = el_def("element_line", "axis.ticks.x"), axis.ticks.x.bottom = el_def("element_line", "axis.ticks.x"), diff --git a/R/theme.r b/R/theme.r index 16022f4a44..f9d461f1da 100644 --- a/R/theme.r +++ b/R/theme.r @@ -47,7 +47,8 @@ #' `axis.ticks.y.left`, `axis.ticks.y.right`). `axis.ticks.*.*` inherits from #' `axis.ticks.*` which inherits from `axis.ticks`, which in turn inherits #' from `line` -#' @param axis.ticks.length length of tick marks (`unit`) +#' @param axis.ticks.length,axis.ticks.length.x,axis.ticks.length.x.top,axis.ticks.length.x.bottom,axis.ticks.length.y,axis.ticks.length.y.left,axis.ticks.length.y.right +#' length of tick marks (`unit`) #' @param axis.line,axis.line.x,axis.line.x.top,axis.line.x.bottom,axis.line.y,axis.line.y.left,axis.line.y.right #' lines along axes ([element_line()]). Specify lines along all axes (`axis.line`), #' lines for each plane (using `axis.line.x` or `axis.line.y`), or individually @@ -192,12 +193,21 @@ #' ) #' #' # Axes ---------------------------------------------------------------------- +#' # Change styles of axes texts and lines #' p1 + theme(axis.line = element_line(size = 3, colour = "grey80")) #' p1 + theme(axis.text = element_text(colour = "blue")) #' p1 + theme(axis.ticks = element_line(size = 2)) -#' p1 + theme(axis.ticks.length = unit(.25, "cm")) +#' +#' # Change the appearance of the y-axis title #' p1 + theme(axis.title.y = element_text(size = rel(1.5), angle = 90)) #' +#' # Make ticks point outwards on y-axis and inwards on x-axis +#' p1 + theme( +#' axis.ticks.length.y = unit(.25, "cm"), +#' axis.ticks.length.x = unit(-.25, "cm"), +#' axis.text.x = element_text(margin = margin(t = .3, unit = "cm")) +#' ) +#' #' \donttest{ #' # Legend -------------------------------------------------------------------- #' p2 <- ggplot(mtcars, aes(wt, mpg)) + @@ -275,6 +285,12 @@ theme <- function(line, axis.ticks.y.left, axis.ticks.y.right, axis.ticks.length, + axis.ticks.length.x, + axis.ticks.length.x.top, + axis.ticks.length.x.bottom, + axis.ticks.length.y, + axis.ticks.length.y.left, + axis.ticks.length.y.right, axis.line, axis.line.x, axis.line.x.top, diff --git a/man/theme.Rd b/man/theme.Rd index cc99c50f5b..2591925f4a 100644 --- a/man/theme.Rd +++ b/man/theme.Rd @@ -10,6 +10,8 @@ theme(line, rect, text, title, aspect.ratio, axis.title, axis.title.x, axis.text.x.bottom, axis.text.y, axis.text.y.left, axis.text.y.right, axis.ticks, axis.ticks.x, axis.ticks.x.top, axis.ticks.x.bottom, axis.ticks.y, axis.ticks.y.left, axis.ticks.y.right, axis.ticks.length, + axis.ticks.length.x, axis.ticks.length.x.top, axis.ticks.length.x.bottom, + axis.ticks.length.y, axis.ticks.length.y.left, axis.ticks.length.y.right, axis.line, axis.line.x, axis.line.x.top, axis.line.x.bottom, axis.line.y, axis.line.y.left, axis.line.y.right, legend.background, legend.margin, legend.spacing, legend.spacing.x, legend.spacing.y, legend.key, @@ -60,7 +62,7 @@ for each axis (using \code{axis.ticks.x.bottom}, \code{axis.ticks.x.top}, \code{axis.ticks.*} which inherits from \code{axis.ticks}, which in turn inherits from \code{line}} -\item{axis.ticks.length}{length of tick marks (\code{unit})} +\item{axis.ticks.length, axis.ticks.length.x, axis.ticks.length.x.top, axis.ticks.length.x.bottom, axis.ticks.length.y, axis.ticks.length.y.left, axis.ticks.length.y.right}{length of tick marks (\code{unit})} \item{axis.line, axis.line.x, axis.line.x.top, axis.line.x.bottom, axis.line.y, axis.line.y.left, axis.line.y.right}{lines along axes (\code{\link[=element_line]{element_line()}}). Specify lines along all axes (\code{axis.line}), lines for each plane (using \code{axis.line.x} or \code{axis.line.y}), or individually @@ -252,7 +254,11 @@ p1 + theme( p1 + theme(axis.line = element_line(size = 3, colour = "grey80")) p1 + theme(axis.text = element_text(colour = "blue")) p1 + theme(axis.ticks = element_line(size = 2)) -p1 + theme(axis.ticks.length = unit(.25, "cm")) +p1 + theme( + axis.ticks.length.y = unit(.25, "cm"), + axis.ticks.length.x = unit(-.25, "cm"), + axis.text.x=element_text(margin=margin(t=.3, unit="cm")) +) p1 + theme(axis.title.y = element_text(size = rel(1.5), angle = 90)) \donttest{ diff --git a/tests/figs/themes/ticks_length.svg b/tests/figs/themes/ticks_length.svg new file mode 100644 index 0000000000..54c82ae3b1 --- /dev/null +++ b/tests/figs/themes/ticks_length.svg @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +2.5 +5.0 +7.5 +10.0 + + + + +2.5 +5.0 +7.5 +10.0 + + + + + + + + +2.5 +5.0 +7.5 +10.0 + + + + +2.5 +5.0 +7.5 +10.0 +1:10 +1:10 +1:10 +1:10 + diff --git a/tests/testthat/test-theme.r b/tests/testthat/test-theme.r index 4130bd03e4..68fb73b0bd 100644 --- a/tests/testthat/test-theme.r +++ b/tests/testthat/test-theme.r @@ -354,6 +354,23 @@ test_that("axes can be styled independently", { expect_doppelganger("axes_styling", plot) }) +test_that("axes ticks can have independent lengths", { + plot <- ggplot() + + theme_test() + + geom_point(aes(1:10, 1:10)) + + scale_x_continuous(sec.axis = dup_axis()) + + scale_y_continuous(sec.axis = dup_axis()) + + theme( + axis.ticks.length.x.top = unit(-.5, "cm"), + axis.ticks.length.x.bottom = unit(-.25, "cm"), + axis.ticks.length.y.left = unit(.25, "cm"), + axis.ticks.length.y.right = unit(.5, "cm"), + axis.text.x.bottom = element_text(margin = margin(t = .5, unit = "cm")), + axis.text.x.top = element_text(margin = margin(b = .75, unit = "cm")) + ) + expect_doppelganger("ticks_length", plot) +}) + test_that("strips can be styled independently", { df <- data_frame(x = 1:2, y = 1:2) plot <- ggplot(df, aes(x, y)) +