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

Discrete minor ticks #5777

Merged
merged 9 commits into from
May 20, 2024
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@

# ggplot2 (development version)

* Discrete scales now support `minor_breaks`. This may only make sense in
discrete position scales, where it affects the placement of minor ticks
and minor gridlines (#5434).
* Fixed performance loss when the `.data` pronoun is used in `aes()` (#5730).
* Fixed bug where discrete scales could not map aesthetics only consisting of
`NA`s (#5623)
Expand Down
5 changes: 5 additions & 0 deletions R/guide-axis.R
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,11 @@ GuideAxis <- ggproto(

if (nrow(major) > 0) {
major$.type <- "major"
if (!vec_is(minor$.value, major$.value)) {
# If we have mixed types of values, which may happen in discrete scales,
# discard minor values in favour of the major values.
minor$.value <- NULL
}
vec_rbind(major, minor)
} else {
minor
Expand Down
40 changes: 35 additions & 5 deletions R/scale-.R
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,13 @@
#' Also accepts rlang [lambda][rlang::as_function()] function notation.
#' @param minor_breaks One of:
#' - `NULL` for no minor breaks
#' - `waiver()` for the default breaks (one minor break between
#' each major break)
#' - `waiver()` for the default breaks (none for discrete, one minor break
#' between each major break for continuous)
#' - A numeric vector of positions
#' - A function that given the limits returns a vector of minor breaks. Also
#' accepts rlang [lambda][rlang::as_function()] function notation. When
#' the function has two arguments, it will be given the limits and major
#' breaks.
#' break positions.
#' @param n.breaks An integer guiding the number of major breaks. The algorithm
#' may choose a slightly different number to ensure nice break labels. Will
#' only have an effect if `breaks = waiver()`. Use `NULL` to use the default
Expand Down Expand Up @@ -199,7 +199,8 @@ continuous_scale <- function(aesthetics, scale_name = deprecated(), palette, nam
#' The `r link_book("new scales section", "extensions#sec-new-scales")`
#' @keywords internal
discrete_scale <- function(aesthetics, scale_name = deprecated(), palette, name = waiver(),
breaks = waiver(), labels = waiver(), limits = NULL, expand = waiver(),
breaks = waiver(), minor_breaks = waiver(),
labels = waiver(), limits = NULL, expand = waiver(),
na.translate = TRUE, na.value = NA, drop = TRUE,
guide = "legend", position = "left",
call = caller_call(),
Expand All @@ -217,6 +218,7 @@ discrete_scale <- function(aesthetics, scale_name = deprecated(), palette, name
limits <- allow_lambda(limits)
breaks <- allow_lambda(breaks)
labels <- allow_lambda(labels)
minor_breaks <- allow_lambda(minor_breaks)

if (!is.function(limits) && (length(limits) > 0) && !is.discrete(limits)) {
cli::cli_warn(c(
Expand Down Expand Up @@ -246,6 +248,7 @@ discrete_scale <- function(aesthetics, scale_name = deprecated(), palette, name

name = name,
breaks = breaks,
minor_breaks = minor_breaks,
labels = labels,
drop = drop,
guide = guide,
Expand Down Expand Up @@ -1020,7 +1023,34 @@ ScaleDiscrete <- ggproto("ScaleDiscrete", Scale,
structure(in_domain, pos = match(in_domain, breaks))
},

get_breaks_minor = function(...) NULL,
get_breaks_minor = function(self, n = 2, b = self$break_positions(),
limits = self$get_limits()) {
breaks <- self$minor_breaks
# The default is to draw no minor ticks
if (is.null(breaks %|W|% NULL)) {
return(NULL)
}
if (is.function(breaks)) {
# Ensure function gets supplied numeric limits and breaks
if (!is.numeric(b)) {
b <- self$map(b)
}
if (!is.numeric(limits)) {
limits <- self$map(limits)
limits <- self$dimension(self$expand, limits)
}

# Allow for two types of minor breaks specifications
break_fun <- fetch_ggproto(self, "minor_breaks")
arg_names <- fn_fmls_names(break_fun)
if (length(arg_names) == 1L) {
breaks <- break_fun(limits)
} else {
breaks <- break_fun(limits, b)
}
}
breaks
},

get_labels = function(self, breaks = self$get_breaks()) {
if (self$is_empty()) {
Expand Down
9 changes: 4 additions & 5 deletions R/scale-view.R
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,16 @@
view_scale_primary <- function(scale, limits = scale$get_limits(),
continuous_range = scale$dimension(limits = limits)) {

# continuous_range can be specified in arbitrary order, but
# scales expect the one in ascending order.
continuous_scale_sorted <- sort(continuous_range)
if(!scale$is_discrete()) {
# continuous_range can be specified in arbitrary order, but
# continuous scales expect the one in ascending order.
continuous_scale_sorted <- sort(continuous_range)
breaks <- scale$get_breaks(continuous_scale_sorted)
minor_breaks <- scale$get_breaks_minor(b = breaks, limits = continuous_scale_sorted)
breaks <- censor(breaks, continuous_scale_sorted, only.finite = FALSE)
} else {
breaks <- scale$get_breaks(limits)
minor_breaks <- scale$get_breaks_minor(b = breaks, limits = limits)
}
minor_breaks <- scale$get_breaks_minor(b = breaks, limits = continuous_scale_sorted)
minor_breaks <- censor(minor_breaks, continuous_range, only.finite = FALSE)

ggproto(NULL, ViewScale,
Expand Down
6 changes: 3 additions & 3 deletions man/continuous_scale.Rd

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

13 changes: 13 additions & 0 deletions man/discrete_scale.Rd

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

6 changes: 3 additions & 3 deletions man/scale_continuous.Rd

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

11 changes: 11 additions & 0 deletions man/scale_discrete.Rd

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

6 changes: 3 additions & 3 deletions man/scale_gradient.Rd

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

11 changes: 11 additions & 0 deletions man/scale_grey.Rd

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

11 changes: 11 additions & 0 deletions man/scale_hue.Rd

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

11 changes: 11 additions & 0 deletions man/scale_linetype.Rd

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

11 changes: 11 additions & 0 deletions man/scale_manual.Rd

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

11 changes: 11 additions & 0 deletions man/scale_shape.Rd

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

6 changes: 3 additions & 3 deletions man/scale_size.Rd

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