From 8211e6ced72a2b67d50b92945a395634138166d7 Mon Sep 17 00:00:00 2001 From: Federico Marotta Date: Thu, 19 Jun 2025 11:51:04 +0200 Subject: [PATCH 1/6] Expose shape argument in geom_curve() (#5998) --- R/geom-curve.R | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/R/geom-curve.R b/R/geom-curve.R index dcb7b18003..66442c662b 100644 --- a/R/geom-curve.R +++ b/R/geom-curve.R @@ -6,8 +6,8 @@ GeomCurve <- ggproto( "GeomCurve", GeomSegment, - draw_panel = function(data, panel_params, coord, curvature = 0.5, angle = 90, - ncp = 5, arrow = NULL, arrow.fill = NULL, lineend = "butt", na.rm = FALSE) { + draw_panel = function(data, panel_params, coord, curvature = 0.5, angle = 90, ncp = 5, shape = 0.5, + arrow = NULL, arrow.fill = NULL, lineend = "butt", na.rm = FALSE) { if (!coord$is_linear()) { cli::cli_warn("{.fn geom_curve} is not implemented for non-linear coordinates") @@ -31,11 +31,13 @@ GeomCurve <- ggproto( arrow.fill <- arrow.fill %||% trans$colour + square <- (ncp == 1 && angle == 90) + curveGrob( trans$x, trans$y, trans$xend, trans$yend, default.units = "native", - curvature = curvature, angle = angle, ncp = ncp, - square = FALSE, squareShape = 1, inflect = FALSE, open = TRUE, + curvature = curvature, angle = angle, ncp = ncp, shape = shape, + square = square, squareShape = 1, inflect = FALSE, open = TRUE, gp = gg_par( col = alpha(trans$colour, trans$alpha), fill = alpha(arrow.fill, trans$alpha), From 918ec3e00af22941b21db926e5dc2c93f6b8ef97 Mon Sep 17 00:00:00 2001 From: Federico Marotta Date: Thu, 19 Jun 2025 11:51:58 +0200 Subject: [PATCH 2/6] Add tests for the shape argument in geom_curve() --- tests/testthat/test-geom-curve.R | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/tests/testthat/test-geom-curve.R b/tests/testthat/test-geom-curve.R index 05f959916e..a238dd1751 100644 --- a/tests/testthat/test-geom-curve.R +++ b/tests/testthat/test-geom-curve.R @@ -9,3 +9,27 @@ test_that("geom_curve flipping works", { expect_doppelganger("flipped geom_curve", p + scale_y_reverse()) }) + +test_that("geom_curve shape works", { + + df <- data.frame(x = c(1, 3), xend = c(2, 4), y = c(0, 1), yend = c(2, 1.5)) + + p_0 <- ggplot(df, aes(x, y, xend = xend, yend = yend)) + + geom_curve(arrow = arrow(), shape = 0, ncp = 1, curvature = 1) + + # This will use `square = FALSE` in curveGrob because angle != 90 + p_0_not_square <- ggplot(df, aes(x, y, xend = xend, yend = yend)) + + geom_curve(arrow = arrow(), shape = 0, ncp = 1, curvature = 1, angle = 60) + + p_1 <- ggplot(df, aes(x, y, xend = xend, yend = yend)) + + geom_curve(arrow = arrow(), shape = 1) + + p_m1 <- ggplot(df, aes(x, y, xend = xend, yend = yend)) + + geom_curve(arrow = arrow(), shape = -1, angle = 40) + + expect_doppelganger("shape=0 geom_curve", p_0) + expect_doppelganger("shape=0 geom_curve", p_0_not_square) + expect_doppelganger("shape=1 geom_curve", p_1) + expect_doppelganger("shape=-1 geom_curve", p_m1) + +}) From ab03c4e90713f0cd2fc8671d5583696b43a61e73 Mon Sep 17 00:00:00 2001 From: Federico Marotta Date: Thu, 19 Jun 2025 11:57:13 +0200 Subject: [PATCH 3/6] Add docs for shape argument in geom_curve() --- man/geom_segment.Rd | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/man/geom_segment.Rd b/man/geom_segment.Rd index 3ea613864a..dd8a051bfe 100644 --- a/man/geom_segment.Rd +++ b/man/geom_segment.Rd @@ -29,6 +29,7 @@ geom_curve( curvature = 0.5, angle = 90, ncp = 5, + shape = 0.5, arrow = NULL, arrow.fill = NULL, lineend = "butt", @@ -149,6 +150,10 @@ the default plot specification, e.g. \code{\link[=annotation_borders]{annotation \item{ncp}{The number of control points used to draw the curve. More control points creates a smoother curve.} + +\item{shape}{A numeric vector of values between -1 and 1, which + control the shape of the curve relative to its control points. + See \code{grid.xspline} for more details.} } \description{ \code{geom_segment()} draws a straight line between points (x, y) and From 9ad937fa43f142ed5140d2eeb06ac700fd0fdba8 Mon Sep 17 00:00:00 2001 From: Federico Marotta Date: Sun, 22 Jun 2025 11:28:04 +0200 Subject: [PATCH 4/6] Add example for shape and ncp params in geom_curve() --- R/geom-segment.R | 17 +++++++++++++++++ man/geom_segment.Rd | 17 +++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/R/geom-segment.R b/R/geom-segment.R index 77ca127a44..9a52429659 100644 --- a/R/geom-segment.R +++ b/R/geom-segment.R @@ -94,6 +94,23 @@ GeomSegment <- ggproto( #' arrow = arrow(length = unit(0.03, "npc")) #' ) #' +#' # The `shape` and `ncp` arguments of geom_curve control the sharpness of the spline +#' b + +#' geom_curve( +#' aes(x = x1, y = y1, xend = x2, yend = y2, colour = "ncp = 5"), +#' data = df, +#' curvature = 1, +#' shape = 0, +#' ncp = 5 +#' ) + +#' geom_curve( +#' aes(x = x1, y = y1, xend = x2, yend = y2, colour = "ncp = 1"), +#' data = df, +#' curvature = 1, +#' shape = 0, +#' ncp = 1 +#' ) +#' #' if (requireNamespace('maps', quietly = TRUE)) { #' ggplot(seals, aes(long, lat)) + #' geom_segment(aes(xend = long + delta_long, yend = lat + delta_lat), diff --git a/man/geom_segment.Rd b/man/geom_segment.Rd index dd8a051bfe..65b7fc7da3 100644 --- a/man/geom_segment.Rd +++ b/man/geom_segment.Rd @@ -182,6 +182,23 @@ b + geom_curve( arrow = arrow(length = unit(0.03, "npc")) ) +# The `shape` and `ncp` arguments of geom_curve control the sharpness of the spline +b + + geom_curve( + aes(x = x1, y = y1, xend = x2, yend = y2, colour = "ncp = 5"), + data = df, + curvature = 1, + shape = 0, + ncp = 5 + ) + + geom_curve( + aes(x = x1, y = y1, xend = x2, yend = y2, colour = "ncp = 1"), + data = df, + curvature = 1, + shape = 0, + ncp = 1 + ) + if (requireNamespace('maps', quietly = TRUE)) { ggplot(seals, aes(long, lat)) + geom_segment(aes(xend = long + delta_long, yend = lat + delta_lat), From 76edc8faff739bec413f200c383ed98b886ca92e Mon Sep 17 00:00:00 2001 From: Federico Marotta Date: Sun, 22 Jun 2025 11:31:09 +0200 Subject: [PATCH 5/6] Apply suggestion to improve geom_curve() test --- tests/testthat/test-geom-curve.R | 49 +++++++++++++++++++++----------- 1 file changed, 32 insertions(+), 17 deletions(-) diff --git a/tests/testthat/test-geom-curve.R b/tests/testthat/test-geom-curve.R index a238dd1751..0e0aa41c15 100644 --- a/tests/testthat/test-geom-curve.R +++ b/tests/testthat/test-geom-curve.R @@ -14,22 +14,37 @@ test_that("geom_curve shape works", { df <- data.frame(x = c(1, 3), xend = c(2, 4), y = c(0, 1), yend = c(2, 1.5)) - p_0 <- ggplot(df, aes(x, y, xend = xend, yend = yend)) + - geom_curve(arrow = arrow(), shape = 0, ncp = 1, curvature = 1) - - # This will use `square = FALSE` in curveGrob because angle != 90 - p_0_not_square <- ggplot(df, aes(x, y, xend = xend, yend = yend)) + - geom_curve(arrow = arrow(), shape = 0, ncp = 1, curvature = 1, angle = 60) - - p_1 <- ggplot(df, aes(x, y, xend = xend, yend = yend)) + - geom_curve(arrow = arrow(), shape = 1) - - p_m1 <- ggplot(df, aes(x, y, xend = xend, yend = yend)) + - geom_curve(arrow = arrow(), shape = -1, angle = 40) - - expect_doppelganger("shape=0 geom_curve", p_0) - expect_doppelganger("shape=0 geom_curve", p_0_not_square) - expect_doppelganger("shape=1 geom_curve", p_1) - expect_doppelganger("shape=-1 geom_curve", p_m1) + p <- ggplot(df) + + geom_curve( + aes(x, y, xend = xend, yend = yend, color = "square"), + curvature = 1, + shape = 0, + ncp = 1 + ) + + geom_curve( + # This layer will use `square = FALSE` in curveGrob because angle != 90 + aes(x, y, xend = xend, yend = yend, color = "square tilted"), + angle = 60, + curvature = 1, + shape = 0, + ncp = 1 + ) + + geom_curve( + aes(x, y, xend = xend, yend = yend, color = "spline cubic"), + curvature = -.5, + angle = 40, + shape = 1, + ncp = 1 + ) + + geom_curve( + aes(x, y, xend = xend, yend = yend, color = "spline interpolating"), + curvature = -.5, + angle = 40, + shape = -1, + ncp = 1 + ) + + NULL + + expect_doppelganger("multishape geom_curve", p) }) From 196f1abf14a017eed4eeb2f95829cf7644153ff3 Mon Sep 17 00:00:00 2001 From: Federico Marotta Date: Sun, 22 Jun 2025 11:32:02 +0200 Subject: [PATCH 6/6] Add snapshot for new geom_curve() test --- .../geom-curve/multishape-geom-curve.svg | 77 +++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 tests/testthat/_snaps/geom-curve/multishape-geom-curve.svg diff --git a/tests/testthat/_snaps/geom-curve/multishape-geom-curve.svg b/tests/testthat/_snaps/geom-curve/multishape-geom-curve.svg new file mode 100644 index 0000000000..3d276e0ff0 --- /dev/null +++ b/tests/testthat/_snaps/geom-curve/multishape-geom-curve.svg @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +0.0 +0.5 +1.0 +1.5 +2.0 + + + + + + + + + +1 +2 +3 +4 +x +y + +colour + + + + + + + + +spline cubic +spline interpolating +square +square tilted +multishape geom_curve + +