diff --git a/NEWS.md b/NEWS.md index c0097b2de4..db00b34331 100644 --- a/NEWS.md +++ b/NEWS.md @@ -221,6 +221,8 @@ the new default in many scales (@teunbrand, #4696). * `guide_axis()` no longer reserves space for blank ticks (@teunbrand, #4722, #6069). +* `geom_abline()` clips to the panel range in the vertical direction too + (@teunbrand, #6086). # ggplot2 3.5.1 diff --git a/R/geom-abline.R b/R/geom-abline.R index e9775e33fb..825d45faf8 100644 --- a/R/geom-abline.R +++ b/R/geom-abline.R @@ -132,12 +132,16 @@ GeomAbline <- ggproto("GeomAbline", Geom, # Ensure the line extends well outside the panel to avoid visible line # ending for thick lines ranges$x <- ranges$x + c(-1, 1) * diff(ranges$x) + ranges$y <- ranges$y + c(-1, 1) * diff(ranges$y) } - data$x <- ranges$x[1] - data$xend <- ranges$x[2] - data$y <- ranges$x[1] * data$slope + data$intercept - data$yend <- ranges$x[2] * data$slope + data$intercept + # Restrict 'x' to where 'y' is in range: x = (y - intercept) / slope + x <- sweep(outer(ranges$y, data$intercept, FUN = "-"), 2, data$slope, FUN = "/") + + data$x <- pmax(ranges$x[1], pmin(x[1, ], x[2, ])) + data$xend <- pmin(ranges$x[2], pmax(x[1, ], x[2, ])) + data$y <- data$x * data$slope + data$intercept + data$yend <- data$xend * data$slope + data$intercept GeomSegment$draw_panel(unique0(data), panel_params, coord, lineend = lineend) }, diff --git a/tests/testthat/_snaps/geom-hline-vline-abline/cartesian-lines-intersect-mid-bars.svg b/tests/testthat/_snaps/geom-hline-vline-abline/cartesian-lines-intersect-mid-bars.svg index f76c20a281..163e6973da 100644 --- a/tests/testthat/_snaps/geom-hline-vline-abline/cartesian-lines-intersect-mid-bars.svg +++ b/tests/testthat/_snaps/geom-hline-vline-abline/cartesian-lines-intersect-mid-bars.svg @@ -39,7 +39,7 @@ - + diff --git a/tests/testthat/_snaps/geom-hline-vline-abline/flipped-lines-intersect-mid-bars.svg b/tests/testthat/_snaps/geom-hline-vline-abline/flipped-lines-intersect-mid-bars.svg index bdda8286d6..cd136306cc 100644 --- a/tests/testthat/_snaps/geom-hline-vline-abline/flipped-lines-intersect-mid-bars.svg +++ b/tests/testthat/_snaps/geom-hline-vline-abline/flipped-lines-intersect-mid-bars.svg @@ -39,7 +39,7 @@ - + diff --git a/tests/testthat/_snaps/geom-hline-vline-abline/polar-lines-intersect-mid-bars.svg b/tests/testthat/_snaps/geom-hline-vline-abline/polar-lines-intersect-mid-bars.svg index c6f3b60763..2f67080988 100644 --- a/tests/testthat/_snaps/geom-hline-vline-abline/polar-lines-intersect-mid-bars.svg +++ b/tests/testthat/_snaps/geom-hline-vline-abline/polar-lines-intersect-mid-bars.svg @@ -48,7 +48,7 @@ - + A B C diff --git a/tests/testthat/test-geom-hline-vline-abline.R b/tests/testthat/test-geom-hline-vline-abline.R index b637cd0a2f..8a324dcf4c 100644 --- a/tests/testthat/test-geom-hline-vline-abline.R +++ b/tests/testthat/test-geom-hline-vline-abline.R @@ -43,6 +43,25 @@ test_that("curved lines in map projections", { ) }) +test_that("geom_abline is clipped to x/y ranges", { + + df <- data.frame(slope = c(-0.2, -1, -5, 5, 1, 0.2)) + + p <- ggplot(df) + + geom_abline(aes(slope = slope, intercept = 0)) + + scale_x_continuous(limits = c(-1, 1), expand = FALSE) + + scale_y_continuous(limits = c(-1, 1), expand = FALSE) + + coord_cartesian(clip = "off") + + data <- layer_grob(p)[[1]] + + x <- c(as.numeric(data$x0), as.numeric(data$x1)) + expect_true(all(x >= 0 & x <= 1)) + + y <- c(as.numeric(data$y0), as.numeric(data$y1)) + expect_true(all(y >= 0 & y <= 1)) +}) + # Warning tests ------------------------------------------------------------ test_that("warnings are thrown when parameters cause mapping and data to be ignored", {