Skip to content

Commit ffdfc23

Browse files
authored
Flip geom_curve() direction in some circumstances (#6332)
* add helper * swap starts/ends when flipping * flip arrows too * simplify arrow flipping * add visual tests * add news bullet
1 parent 63b263b commit ffdfc23

File tree

5 files changed

+181
-0
lines changed

5 files changed

+181
-0
lines changed

NEWS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,7 @@
329329
to retrieve the class via constructor functions (@teunbrand).
330330
* (internal) The ViewScale class has a `make_fixed_copy()` method to permit
331331
copying trained position scales (#3441).
332+
* Improved consistency of curve direction in `geom_curve()` (@teunbrand, #5069)
332333

333334
# ggplot2 3.5.1
334335

R/geom-curve.R

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,15 @@ GeomCurve <- ggproto("GeomCurve", GeomSegment,
5555

5656
trans <- coord$transform(data, panel_params)
5757

58+
flip <- flip_curve(trans, coord, panel_params)
59+
if (flip) {
60+
trans <- rename(trans, c(x = "xend", xend = "x", y = "yend", yend = "y"))
61+
if (!is.null(arrow)) {
62+
# Flip end where arrow appears (2 = last, 1 = first, 3 = both)
63+
arrow$ends <- match(arrow$ends, c(2, 1, 3))
64+
}
65+
}
66+
5867
arrow.fill <- arrow.fill %||% trans$colour
5968

6069
curveGrob(
@@ -72,3 +81,41 @@ GeomCurve <- ggproto("GeomCurve", GeomSegment,
7281
)
7382
}
7483
)
84+
85+
# Helper function for determining whether curves should swap segment ends to
86+
# keep curvature consistent over transformations
87+
flip_curve <- function(data, coord, params) {
88+
flip <- FALSE
89+
90+
# Figure implicit flipping transformations in coords
91+
if (inherits(coord, "CoordFlip")) {
92+
flip <- !flip
93+
} else if (inherits(coord, "CoordTrans")) {
94+
if (identical(coord$trans$x$name, "reverse")) {
95+
flip <- !flip
96+
}
97+
if (identical(coord$trans$y$name, "reverse")) {
98+
flip <- !flip
99+
}
100+
}
101+
102+
# We don't flip when none or both directions are reversed
103+
if ((coord$reverse %||% "none") %in% c("x", "y")) {
104+
flip <- !flip
105+
}
106+
107+
# Check scales for reverse transforms
108+
# Note that polar coords do not have x/y scales, but these are unsupported
109+
# anyway
110+
fn <- params$x$get_transformation
111+
if (is.function(fn) && identical(fn()$name, "reverse")) {
112+
flip <- !flip
113+
}
114+
115+
fn <- params$y$get_transformation
116+
if (is.function(fn) && identical(fn()$name, "reverse")) {
117+
flip <- !flip
118+
}
119+
120+
flip
121+
}
Lines changed: 61 additions & 0 deletions
Loading
Lines changed: 61 additions & 0 deletions
Loading

tests/testthat/test-geom-curve.R

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
test_that("geom_curve flipping works", {
2+
3+
df <- data.frame(x = c(1, 2), xend = c(2, 3), y = 1, yend = c(2, 1.5))
4+
5+
p <- ggplot(df, aes(x, y, xend = xend, yend = yend)) +
6+
geom_curve(arrow = arrow())
7+
8+
expect_doppelganger("standard geom_curve", p)
9+
expect_doppelganger("flipped geom_curve", p + scale_y_reverse())
10+
11+
})

0 commit comments

Comments
 (0)