diff --git a/NEWS.md b/NEWS.md index 7c8a921832..c99bf6b9a5 100644 --- a/NEWS.md +++ b/NEWS.md @@ -124,6 +124,8 @@ * `geom_hline()` and `geom_vline()` now have `position` argument (@yutannihilation, #4285). * New function `get_strip_labels()` to retrieve facet labels (@teunbrand, #4979) +* Fixed bug in `position_dodge2()`'s identification of range overlaps + (@teunbrand, #5938, #4327). # ggplot2 3.5.1 diff --git a/R/position-dodge2.R b/R/position-dodge2.R index bf5f18c56d..e8e291c62c 100644 --- a/R/position-dodge2.R +++ b/R/position-dodge2.R @@ -132,14 +132,19 @@ pos_dodge2 <- function(df, width, n = NULL, padding = 0.1) { # Find groups of overlapping elements that need to be dodged from one another find_x_overlaps <- function(df) { - overlaps <- numeric(nrow(df)) - overlaps[1] <- counter <- 1 - for (i in seq_asc(2, nrow(df))) { - if (is.na(df$xmin[i]) || is.na(df$xmax[i - 1]) || df$xmin[i] >= df$xmax[i - 1]) { - counter <- counter + 1 - } - overlaps[i] <- counter - } - overlaps + start <- df$xmin + nonzero <- df$xmax != df$xmin + missing <- is.na(df$xmin) | is.na(df$xmax) + start <- vec_fill_missing(start, "downup") + end <- vec_fill_missing(df$xmax, "downup") + + # For end we take largest end seen so far of previous observation + end <- cummax(c(end[1], end[-nrow(df)])) + # Start new group when 'start >= end' for non zero-width ranges + # For zero-width ranges, start must be strictly larger than end + overlaps <- cumsum(start > end | (start == end & nonzero)) + # Missing ranges always get separate group + overlaps[missing] <- seq_len(sum(missing)) + max(overlaps, na.rm = TRUE) + match(overlaps, unique0(overlaps)) } diff --git a/tests/testthat/test-position-dodge2.R b/tests/testthat/test-position-dodge2.R index d1e54a37fc..5377f14b2d 100644 --- a/tests/testthat/test-position-dodge2.R +++ b/tests/testthat/test-position-dodge2.R @@ -118,3 +118,11 @@ test_that("groups are different when two blocks have externall touching point",{ ) expect_equal(find_x_overlaps(df1), seq_len(2)) }) + +test_that("overlaps are identified correctly", { + df <- data.frame( + xmin = c(1, 2, 3, 5), + xmax = c(4, 3, 4, 6) + ) + expect_equal(find_x_overlaps(df), c(1, 1, 1, 2)) +})