diff --git a/NEWS.md b/NEWS.md index 2e9b4e63eb..1bf751fdc9 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,8 @@ # ggplot2 (development version) +* Better handling of the `guide_axis_logticks(negative.small)` parameter when + scale limits have small maximum (@teunbrand, #6121). +* Fixed bug where the `ggplot2::`-prefix did not work with `stage()` * New roxygen tag `@aesthetics` that takes a Geom, Stat or Position class and generates an 'Aesthetics' section. * `annotation_borders()` replaces the now-deprecated `borders()` diff --git a/R/guide-axis-logticks.R b/R/guide-axis-logticks.R index 42caad3ce5..41fbbe4ab9 100644 --- a/R/guide-axis-logticks.R +++ b/R/guide-axis-logticks.R @@ -17,6 +17,8 @@ NULL #' keep the default `NULL` argument. #' @param negative.small When the scale limits include 0 or negative numbers, #' what should be the smallest absolute value that is marked with a tick? +#' If `NULL` (default), will be the smallest of 0.1 or 0.1 times the absolute +#' scale maximum. #' @param short.theme A theme [element][element_line()] for customising the #' display of the shortest ticks. Must be a line or blank element, and #' it inherits from the `axis.minor.ticks` setting for the relevant position. @@ -69,7 +71,7 @@ guide_axis_logticks <- function( mid = 1.5, short = 0.75, prescale.base = NULL, - negative.small = 0.1, + negative.small = NULL, short.theme = element_line(), expanded = TRUE, cap = "none", @@ -186,40 +188,12 @@ GuideAxisLogticks <- ggproto( # Reconstruct original range limits <- transformation$inverse(scale$get_limits()) - has_negatives <- any(limits <= 0) - if (!has_negatives) { - start <- floor(log10(min(limits))) - 1L - end <- ceiling(log10(max(limits))) + 1L - } else { - params$negative_small <- params$negative_small %||% 0.1 - start <- floor(log10(abs(params$negative_small))) - end <- ceiling(log10(max(abs(limits)))) + 1L - } + ticks <- minor_breaks_log(smallest = params$negative_small)(limits) + tick_type <- match(attr(ticks, "detail"), c(10, 5, 1)) + ticks <- transformation$transform(ticks) - # Calculate tick marks - tens <- 10^seq(start, end, by = 1) - fives <- tens * 5 - ones <- as.vector(outer(setdiff(2:9, 5), tens)) - - if (has_negatives) { - # Filter and mirror ticks around 0 - tens <- tens[tens >= params$negative_small] - tens <- c(tens, -tens, 0) - fives <- fives[fives >= params$negative_small] - fives <- c(fives, -fives) - ones <- ones[ones >= params$negative_small] - ones <- c(ones, -ones) - } - - # Set ticks back into transformed space - ticks <- transformation$transform(c(tens, fives, ones)) - nticks <- c(length(tens), length(fives), length(ones)) - - logkey <- data_frame0( - !!aesthetic := ticks, - .type = rep(1:3, times = nticks) - ) + logkey <- data_frame0(!!aesthetic := ticks, .type = tick_type) # Discard out-of-bounds ticks range <- if (params$expanded) scale$continuous_range else scale$get_limits() diff --git a/man/guide_axis_logticks.Rd b/man/guide_axis_logticks.Rd index 922e354566..2fe96f0052 100644 --- a/man/guide_axis_logticks.Rd +++ b/man/guide_axis_logticks.Rd @@ -9,7 +9,7 @@ guide_axis_logticks( mid = 1.5, short = 0.75, prescale.base = NULL, - negative.small = 0.1, + negative.small = NULL, short.theme = element_line(), expanded = TRUE, cap = "none", @@ -33,7 +33,9 @@ When using a log-transform in the position scale or in \code{coord_transform()}, keep the default \code{NULL} argument.} \item{negative.small}{When the scale limits include 0 or negative numbers, -what should be the smallest absolute value that is marked with a tick?} +what should be the smallest absolute value that is marked with a tick? +If \code{NULL} (default), will be the smallest of 0.1 or 0.1 times the absolute +scale maximum.} \item{short.theme}{A theme \link[=element_line]{element} for customising the display of the shortest ticks. Must be a line or blank element, and diff --git a/tests/testthat/test-guide-axis.R b/tests/testthat/test-guide-axis.R index 5342332e02..836df8bb2d 100644 --- a/tests/testthat/test-guide-axis.R +++ b/tests/testthat/test-guide-axis.R @@ -90,6 +90,11 @@ test_that("guide_axis_logticks calculates appropriate ticks", { expect_equal(unlog, c(-rev(outcome), 0, outcome)) expect_equal(key$.type, rep(c(1,2,3), c(7, 4, 28))) + # Test very small pseudo_log (#6121) + scale <- test_scale(transform_pseudo_log(sigma = 1e-5), c(0, 1e-10)) + key <- train_guide(guide_axis_logticks(), scale)$logkey + expect_gte(nrow(key), 1) + # Test expanded argument scale <- test_scale(transform_log10(), c(20, 900)) scale$continuous_range <- c(1, 3)