Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Minor ticks #5287

Merged
merged 28 commits into from
Oct 24, 2023
Merged
Show file tree
Hide file tree
Changes from 27 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
b463617
Swap tick anchorpoint
teunbrand Apr 26, 2023
6ef9990
`Guide$build_ticks()` accepts a length value
teunbrand Apr 26, 2023
c459613
Add tick arguments
teunbrand Apr 26, 2023
4a7f7b9
`GuideAxis$extract_key()` can get minor ticks
teunbrand Apr 26, 2023
599632e
`GuideAxis$build_ticks()` makes draws minor ticks
teunbrand Apr 26, 2023
62d9f44
Fix bug with unlabelled breaks
teunbrand Apr 26, 2023
d28b62e
Adjust tick spacing
teunbrand Apr 26, 2023
e4a4602
Finishing touches
teunbrand Apr 26, 2023
b749e80
Add test
teunbrand Apr 26, 2023
a69fedf
Accept tick-ordering changes in snapshots
teunbrand Apr 26, 2023
6614ce2
Add news bullet
teunbrand Apr 26, 2023
ec3de58
Fix expression labels
teunbrand Apr 26, 2023
0d2f874
Document
teunbrand Apr 26, 2023
640c788
Update snapshot
teunbrand Apr 26, 2023
926e1df
Resolve merge
teunbrand Jun 22, 2023
e98ab9c
Merge latest
teunbrand Aug 27, 2023
c368ab9
minor tick theme options
teunbrand Aug 27, 2023
ee130e7
Revise minor to use theme
teunbrand Aug 27, 2023
5acbb1d
Merge branch 'main' into minor_ticks
teunbrand Sep 12, 2023
d54aa88
Resolve merge
teunbrand Sep 12, 2023
144183a
Use `rel()` for minor ticks
teunbrand Sep 12, 2023
10354e1
Biparental inheritance for minor tick length leaf nodes
teunbrand Sep 12, 2023
2eae409
change minor break extraction
teunbrand Oct 4, 2023
0f15a41
Skip tick calculation with blank elements
teunbrand Oct 4, 2023
d4240bb
clean up axis ticks building
teunbrand Oct 4, 2023
d302663
change measurement function to width_cm/height_cm
teunbrand Oct 4, 2023
f7e8afc
Remove redundant function
teunbrand Oct 4, 2023
9676012
Merge branch 'main' into minor_ticks
teunbrand Oct 24, 2023
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
1 change: 1 addition & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@
* More informative error for mismatched
`direction`/`theme(legend.direction = ...)` arguments (#4364, #4930).
* `guide_coloursteps()` and `guide_bins()` sort breaks (#5152).
* `guide_axis()` gains a `minor.ticks` argument to draw minor ticks (#4387).
* `guide_axis()` gains a `cap` argument that can be used to trim the
axis line to extreme breaks (#4907).
* `guide_colourbar()` and `guide_coloursteps()` merge properly when one
Expand Down
16 changes: 11 additions & 5 deletions R/guide-.R
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,14 @@ Guide <- ggproto(
},

# Renders tickmarks
build_ticks = function(key, elements, params, position = params$position) {
build_ticks = function(key, elements, params, position = params$position,
length = elements$ticks_length) {
if (!inherits(elements, "element")) {
elements <- elements$ticks
}
if (!inherits(elements, "element_line")) {
return(zeroGrob())
}

if (!is.list(key)) {
breaks <- key
Expand All @@ -365,8 +372,7 @@ Guide <- ggproto(
return(zeroGrob())
}

tick_len <- rep(elements$ticks_length %||% unit(0.2, "npc"),
length.out = n_breaks)
tick_len <- rep(length %||% unit(0.2, "npc"), length.out = n_breaks)

# Resolve mark
mark <- unit(rep(breaks, each = 2), "npc")
Expand All @@ -375,12 +381,12 @@ Guide <- ggproto(
pos <- unname(c(top = 1, bottom = 0, left = 0, right = 1)[position])
dir <- -2 * pos + 1
pos <- unit(rep(pos, 2 * n_breaks), "npc")
dir <- rep(vec_interleave(0, dir), n_breaks) * tick_len
dir <- rep(vec_interleave(dir, 0), n_breaks) * tick_len
tick <- pos + dir

# Build grob
flip_element_grob(
elements$ticks,
elements,
x = tick, y = mark,
id.lengths = rep(2, n_breaks),
flip = position %in% c("top", "bottom")
Expand Down
99 changes: 78 additions & 21 deletions R/guide-axis.R
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
#' @param n.dodge The number of rows (for vertical axes) or columns (for
#' horizontal axes) that should be used to render the labels. This is
#' useful for displaying labels that would otherwise overlap.
#' @param minor.ticks Whether to draw the minor ticks (`TRUE`) or not draw
#' minor ticks (`FALSE`, default).
#' @param cap A `character` to cut the axis line back to the last breaks. Can
#' be `"none"` (default) to draw the axis line along the whole panel, or
#' `"upper"` and `"lower"` to draw the axis to the upper or lower break, or
Expand Down Expand Up @@ -42,23 +44,23 @@
#' # can also be used to add a duplicate guide
#' p + guides(x = guide_axis(n.dodge = 2), y.sec = guide_axis())
guide_axis <- function(title = waiver(), check.overlap = FALSE, angle = NULL,
n.dodge = 1, cap = "none", order = 0,
position = waiver()) {

n.dodge = 1, minor.ticks = FALSE, cap = "none",
order = 0, position = waiver()) {
check_bool(minor.ticks)
if (is.logical(cap)) {
check_bool(cap)
cap <- if (cap) "both" else "none"
}
cap <- arg_match0(cap, c("none", "both", "upper", "lower"))


new_guide(
title = title,

# customisations
check.overlap = check.overlap,
angle = angle,
n.dodge = n.dodge,
minor.ticks = minor.ticks,
cap = cap,

# parameter
Expand Down Expand Up @@ -87,6 +89,7 @@ GuideAxis <- ggproto(
direction = NULL,
angle = NULL,
n.dodge = 1,
minor.ticks = FALSE,
cap = "none",
order = 0,
check.overlap = FALSE
Expand All @@ -100,9 +103,37 @@ GuideAxis <- ggproto(
line = "axis.line",
text = "axis.text",
ticks = "axis.ticks",
ticks_length = "axis.ticks.length"
minor = "axis.minor.ticks",
major_length = "axis.ticks.length",
minor_length = "axis.minor.ticks.length"
),

extract_key = function(scale, aesthetic, minor.ticks, ...) {
major <- Guide$extract_key(scale, aesthetic, ...)
if (!minor.ticks) {
return(major)
}

minor_breaks <- scale$get_breaks_minor()
minor_breaks <- setdiff(minor_breaks, major$.value)
minor_breaks <- minor_breaks[is.finite(minor_breaks)]

if (length(minor_breaks) < 1) {
return(major)
}

minor <- data_frame0(!!aesthetic := scale$map(minor_breaks))
minor$.value <- minor_breaks
minor$.type <- "minor"

if (nrow(major) > 0) {
major$.type <- "major"
vec_rbind(major, minor)
} else {
minor
}
},

extract_params = function(scale, params, ...) {
params$name <- paste0(params$name, "_", params$aesthetic)
params
Expand Down Expand Up @@ -190,7 +221,7 @@ GuideAxis <- ggproto(
},

setup_elements = function(params, elements, theme) {
axis_elem <- c("line", "text", "ticks", "ticks_length")
axis_elem <- c("line", "text", "ticks", "minor", "major_length", "minor_length")
is_char <- vapply(elements[axis_elem], is.character, logical(1))
axis_elem <- axis_elem[is_char]
elements[axis_elem] <- lapply(
Expand Down Expand Up @@ -230,26 +261,17 @@ GuideAxis <- ggproto(
"horizontal"
}

# TODO: delete following comment at some point:
# I found the 'position_*'/'non-position_*' and '*_dim' names confusing.
# For my own understanding, these have been renamed as follows:
# * 'aes' and 'orth_aes' for the aesthetic direction and the direction
# orthogonal to the aesthetic direction, respectively.
# * 'para_sizes' and 'orth_size(s)' for the dimension parallel to the
# aesthetic and orthogonal to the aesthetic respectively.
# I also tried to trim down the verbosity of the variable names a bit

new_params <- c("aes", "orth_aes", "para_sizes", "orth_size", "orth_sizes",
"vertical", "measure_gtable", "measure_text")
if (direction == "vertical") {
params[new_params] <- list(
"y", "x", "heights", "width", "widths",
TRUE, gtable_width, grobWidth
TRUE, gtable_width, width_cm
)
} else {
params[new_params] <- list(
"x", "y", "widths", "height", "heights",
FALSE, gtable_height, grobHeight
FALSE, gtable_height, height_cm
)
}

Expand Down Expand Up @@ -280,7 +302,32 @@ GuideAxis <- ggproto(
)
},

build_ticks = function(key, elements, params, position = params$opposite) {

major <- Guide$build_ticks(
vec_slice(key, (key$.type %||% "major") == "major"),
elements$ticks, params, position,
elements$major_length
)

if (!params$minor.ticks) {
return(major)
}

minor <- Guide$build_ticks(
vec_slice(key, (key$.type %||% "major") == "minor"),
elements$minor, params, position,
elements$minor_length
)
grobTree(major, minor, name = "ticks")
},

build_labels = function(key, elements, params) {

if (".type" %in% names(key)) {
key <- vec_slice(key, key$.type == "major")
}

labels <- validate_labels(key$.label)
n_labels <- length(labels)

Expand Down Expand Up @@ -314,10 +361,20 @@ GuideAxis <- ggproto(

measure <- params$measure_text

length <- elements$ticks_length
spacer <- max(unit(0, "pt"), -1 * length)
labels <- do.call(unit.c, lapply(grobs$labels, measure))
title <- measure(grobs$title)
# Ticks
major_cm <- convertUnit(elements$major_length, "cm", valueOnly = TRUE)
range <- range(0, major_cm)
if (params$minor.ticks && !inherits(elements$minor, "element_blank")) {
minor_cm <- convertUnit(elements$minor_length, "cm", valueOnly = TRUE)
range <- range(range, minor_cm)
}

length <- unit(range[2], "cm")
spacer <- max(unit(0, "pt"), unit(-1 * diff(range), "cm"))

# Text
labels <- unit(measure(grobs$label), "cm")
title <- unit(measure(grobs$title), "cm")

sizes <- unit.c(length, spacer, labels, title)
if (params$lab_first) {
Expand Down
3 changes: 3 additions & 0 deletions R/theme-defaults.R
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ theme_grey <- function(base_size = 11, base_family = "",
axis.ticks.length.y = NULL,
axis.ticks.length.y.left = NULL,
axis.ticks.length.y.right = NULL,
axis.minor.ticks.length = rel(0.75),
axis.title.x = element_text(
margin = margin(t = half_line / 2),
vjust = 1
Expand Down Expand Up @@ -478,6 +479,7 @@ theme_void <- function(base_size = 11, base_family = "",
axis.ticks.length.y = NULL,
axis.ticks.length.y.left = NULL,
axis.ticks.length.y.right = NULL,
axis.minor.ticks.length = unit(0, "pt"),
legend.box = NULL,
legend.key.size = unit(1.2, "lines"),
legend.position = "right",
Expand Down Expand Up @@ -559,6 +561,7 @@ theme_test <- function(base_size = 11, base_family = "",
axis.ticks.length.y = NULL,
axis.ticks.length.y.left = NULL,
axis.ticks.length.y.right = NULL,
axis.minor.ticks.length = rel(0.75),
axis.title.x = element_text(
margin = margin(t = half_line / 2),
vjust = 1
Expand Down
25 changes: 25 additions & 0 deletions R/theme-elements.R
Original file line number Diff line number Diff line change
Expand Up @@ -441,32 +441,57 @@ el_def <- function(class = NULL, inherit = NULL, description = NULL) {
axis.line.y = el_def("element_line", "axis.line"),
axis.line.y.left = el_def("element_line", "axis.line.y"),
axis.line.y.right = el_def("element_line", "axis.line.y"),

axis.text.x = el_def("element_text", "axis.text"),
axis.text.x.top = el_def("element_text", "axis.text.x"),
axis.text.x.bottom = el_def("element_text", "axis.text.x"),
axis.text.y = el_def("element_text", "axis.text"),
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(c("unit", "rel"), "axis.ticks.length"),
axis.ticks.length.x.top = el_def(c("unit", "rel"), "axis.ticks.length.x"),
axis.ticks.length.x.bottom = el_def(c("unit", "rel"), "axis.ticks.length.x"),
axis.ticks.length.y = el_def(c("unit", "rel"), "axis.ticks.length"),
axis.ticks.length.y.left = el_def(c("unit", "rel"), "axis.ticks.length.y"),
axis.ticks.length.y.right = el_def(c("unit", "rel"), "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"),
axis.ticks.y = el_def("element_line", "axis.ticks"),
axis.ticks.y.left = el_def("element_line", "axis.ticks.y"),
axis.ticks.y.right = el_def("element_line", "axis.ticks.y"),

axis.title.x = el_def("element_text", "axis.title"),
axis.title.x.top = el_def("element_text", "axis.title.x"),
axis.title.x.bottom = el_def("element_text", "axis.title.x"),
axis.title.y = el_def("element_text", "axis.title"),
axis.title.y.left = el_def("element_text", "axis.title.y"),
axis.title.y.right = el_def("element_text", "axis.title.y"),

axis.minor.ticks.x.top = el_def("element_line", "axis.ticks.x.top"),
axis.minor.ticks.x.bottom = el_def("element_line", "axis.ticks.x.bottom"),
axis.minor.ticks.y.left = el_def("element_line", "axis.ticks.y.left"),
axis.minor.ticks.y.right = el_def("element_line", "axis.ticks.y.right"),

axis.minor.ticks.length = el_def(c("unit", "rel")),
axis.minor.ticks.length.x = el_def(c("unit", "rel"), "axis.minor.ticks.length"),
axis.minor.ticks.length.x.top = el_def(
c("unit", "rel"), c("axis.minor.ticks.length.x", "axis.ticks.length.x.top")
),
axis.minor.ticks.length.x.bottom = el_def(
c("unit", "rel"), c("axis.minor.ticks.length.x", "axis.ticks.length.x.bottom")
),
axis.minor.ticks.length.y = el_def(c("unit", "rel"), "axis.minor.ticks.length"),
axis.minor.ticks.length.y.left = el_def(
c("unit", "rel"), c("axis.minor.ticks.length.y", "axis.ticks.length.y.left")
),
axis.minor.ticks.length.y.right = el_def(
c("unit", "rel"), c("axis.minor.ticks.length.y", "axis.ticks.length.y.right")
),

legend.background = el_def("element_rect", "rect"),
legend.margin = el_def("margin"),
legend.spacing = el_def("unit"),
Expand Down
16 changes: 16 additions & 0 deletions R/theme.R
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,13 @@
#' `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.minor.ticks.x.top,axis.minor.ticks.x.bottom,axis.minor.ticks.y.left,axis.minor.ticks.y.right,
#' minor tick marks along axes ([element_line()]). `axis.minor.ticks.*.*`
#' inherit from the corresponding major ticks `axis.ticks.*.*`.
#' @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.minor.ticks.length,axis.minor.ticks.length.x,axis.minor.ticks.length.x.top,axis.minor.ticks.length.x.bottom,axis.minor.ticks.length.y,axis.minor.ticks.length.y.left,axis.minor.ticks.length.y.right
#' length of minor tick marks (`unit`), or relative to `axis.ticks.length` when provided with `rel()`.
#' @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
Expand Down Expand Up @@ -302,13 +307,24 @@ theme <- function(line,
axis.ticks.y,
axis.ticks.y.left,
axis.ticks.y.right,
axis.minor.ticks.x.top,
axis.minor.ticks.x.bottom,
axis.minor.ticks.y.left,
axis.minor.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.minor.ticks.length,
axis.minor.ticks.length.x,
axis.minor.ticks.length.x.top,
axis.minor.ticks.length.x.bottom,
axis.minor.ticks.length.y,
axis.minor.ticks.length.y.left,
axis.minor.ticks.length.y.right,
axis.line,
axis.line.x,
axis.line.x.top,
Expand Down
4 changes: 4 additions & 0 deletions man/guide_axis.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading