diff --git a/NEWS.md b/NEWS.md
index 3636474947..41949e533b 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -1,5 +1,8 @@
# ggplot2 (development version)
+* Secondary axis ticks are now positioned more precisely, removing small visual
+ artefacts with alignment between grid and ticks (@thomasp85, #3576)
+
* Improve `stat_function` documentation regarding `xlim` argument. (@92amartins, #4474)
* `qplot()` is now formally deprecated (@yutannihilation, #3956).
diff --git a/R/axis-secondary.R b/R/axis-secondary.R
index 626f33c37a..297290d54b 100644
--- a/R/axis-secondary.R
+++ b/R/axis-secondary.R
@@ -210,6 +210,16 @@ AxisSecondary <- ggproto("AxisSecondary", NULL,
# Create mapping between primary and secondary range
full_range <- self$transform_range(old_range)
+ # Remove duplicates in the expanded area of the range that can arise if
+ # the transformation is non-monotonic in the expansion. The split ensures
+ # the middle duplicated are kept
+ duplicates <- c(
+ !duplicated(full_range[seq_len(self$detail/2)], fromLast = TRUE),
+ !duplicated(full_range[-seq_len(self$detail/2)])
+ )
+ old_range <- old_range[duplicates]
+ full_range <- full_range[duplicates]
+
# Get break info for the secondary axis
new_range <- range(full_range, na.rm = TRUE)
@@ -226,8 +236,7 @@ AxisSecondary <- ggproto("AxisSecondary", NULL,
# Map the break values back to their correct position on the primary scale
if (!is.null(range_info$major_source)) {
- old_val <- lapply(range_info$major_source, function(x) which.min(abs(full_range - x)))
- old_val <- old_range[unlist(old_val)]
+ old_val <- approx(full_range, old_range, range_info$major_source)$y
old_val_trans <- scale$trans$transform(old_val)
# rescale values from 0 to 1
@@ -243,8 +252,7 @@ AxisSecondary <- ggproto("AxisSecondary", NULL,
}
if (!is.null(range_info$minor_source)) {
- old_val_minor <- lapply(range_info$minor_source, function(x) which.min(abs(full_range - x)))
- old_val_minor <- old_range[unlist(old_val_minor)]
+ old_val_minor <- approx(full_range, old_range, range_info$minor_source)$y
old_val_minor_trans <- scale$trans$transform(old_val_minor)
range_info$minor[] <- round(
diff --git a/tests/testthat/_snaps/coord-flip/turning-off-secondary-title-with-coord-flip.svg b/tests/testthat/_snaps/coord-flip/turning-off-secondary-title-with-coord-flip.svg
index 808508ac55..a67dbd8469 100644
--- a/tests/testthat/_snaps/coord-flip/turning-off-secondary-title-with-coord-flip.svg
+++ b/tests/testthat/_snaps/coord-flip/turning-off-secondary-title-with-coord-flip.svg
@@ -74,18 +74,18 @@
-
-
-
-
-
-
-10
-15
-20
-25
-30
-35
+
+
+
+
+
+
+10
+15
+20
+25
+30
+35
diff --git a/tests/testthat/_snaps/coord-transform/sec-axis-with-coord-trans.svg b/tests/testthat/_snaps/coord-transform/sec-axis-with-coord-trans.svg
index 05714336c7..01f5766194 100644
--- a/tests/testthat/_snaps/coord-transform/sec-axis-with-coord-trans.svg
+++ b/tests/testthat/_snaps/coord-transform/sec-axis-with-coord-trans.svg
@@ -264,18 +264,18 @@
-10
-15
-20
-25
-30
-35
-
-
-
-
-
-
+10
+15
+20
+25
+30
+35
+
+
+
+
+
+
11.31371
16.00000
22.62742
@@ -286,16 +286,16 @@
-
-
-
-
-
-3.5
-4.0
-4.5
-5.0
-5.5
+
+
+
+
+
+3.5
+4.0
+4.5
+5.0
+5.5
diff --git a/tests/testthat/_snaps/guides/guide-axis-customization.svg b/tests/testthat/_snaps/guides/guide-axis-customization.svg
index 5e47dae137..3466dbe6d5 100644
--- a/tests/testthat/_snaps/guides/guide-axis-customization.svg
+++ b/tests/testthat/_snaps/guides/guide-axis-customization.svg
@@ -270,12 +270,12 @@
-
-
-
-20
-40
-30
+
+
+
+20
+40
+30
diff --git a/tests/testthat/_snaps/sec-axis/sec-axis-custom-transform.svg b/tests/testthat/_snaps/sec-axis/sec-axis-custom-transform.svg
index 0d7652d395..7198e3f244 100644
--- a/tests/testthat/_snaps/sec-axis/sec-axis-custom-transform.svg
+++ b/tests/testthat/_snaps/sec-axis/sec-axis-custom-transform.svg
@@ -75,24 +75,24 @@
-
-
-
-
-
-
-
-
-
-0.001
-0.010
-0.100
-0.250
-0.300
-0.350
-0.400
-0.450
-0.500
+
+
+
+
+
+
+
+
+
+0.001
+0.010
+0.100
+0.250
+0.300
+0.350
+0.400
+0.450
+0.500
diff --git a/tests/testthat/_snaps/sec-axis/sec-axis-independent-transformations.svg b/tests/testthat/_snaps/sec-axis/sec-axis-independent-transformations.svg
index c86ec7ebae..6cbf536afe 100644
--- a/tests/testthat/_snaps/sec-axis/sec-axis-independent-transformations.svg
+++ b/tests/testthat/_snaps/sec-axis/sec-axis-independent-transformations.svg
@@ -51,16 +51,16 @@
-5
-10
-15
-20
-25
-
-
-
-
-
+5
+10
+15
+20
+25
+
+
+
+
+
0.2
0.3
0.4
diff --git a/tests/testthat/_snaps/sec-axis/sec-axis-monotonicity-test.svg b/tests/testthat/_snaps/sec-axis/sec-axis-monotonicity-test.svg
index 0ca0860836..931b3cb820 100644
--- a/tests/testthat/_snaps/sec-axis/sec-axis-monotonicity-test.svg
+++ b/tests/testthat/_snaps/sec-axis/sec-axis-monotonicity-test.svg
@@ -59,14 +59,14 @@
-
-
-
-
-1
-2
-3
-4
+
+
+
+
+1
+2
+3
+4
diff --git a/tests/testthat/_snaps/sec-axis/sec-axis-sec-power-transform.svg b/tests/testthat/_snaps/sec-axis/sec-axis-sec-power-transform.svg
index 26401f5fac..5d751307cb 100644
--- a/tests/testthat/_snaps/sec-axis/sec-axis-sec-power-transform.svg
+++ b/tests/testthat/_snaps/sec-axis/sec-axis-sec-power-transform.svg
@@ -57,18 +57,18 @@
--0.25
-0.00
-0.25
-0.50
-0.75
-1.00
-
-
-
-
-
-
+-0.25
+0.00
+0.25
+0.50
+0.75
+1.00
+
+
+
+
+
+
4.950
4.975
5.000
diff --git a/tests/testthat/_snaps/sec-axis/sec-axis-skewed-transform.svg b/tests/testthat/_snaps/sec-axis/sec-axis-skewed-transform.svg
index e138753b76..f22e062f17 100644
--- a/tests/testthat/_snaps/sec-axis/sec-axis-skewed-transform.svg
+++ b/tests/testthat/_snaps/sec-axis/sec-axis-skewed-transform.svg
@@ -152,16 +152,16 @@
-1e-01
-1e+00
-1e+01
-1e+02
-1e+03
-
-
-
-
-
+1e-01
+1e+00
+1e+01
+1e+02
+1e+03
+
+
+
+
+
0.00
0.25
0.50
diff --git a/tests/testthat/_snaps/sec-axis/sec-axis-with-division.svg b/tests/testthat/_snaps/sec-axis/sec-axis-with-division.svg
index 1a684f576a..3fc8711769 100644
--- a/tests/testthat/_snaps/sec-axis/sec-axis-with-division.svg
+++ b/tests/testthat/_snaps/sec-axis/sec-axis-with-division.svg
@@ -289,12 +289,12 @@
-
-
-
-10
-15
-20
+
+
+
+10
+15
+20
diff --git a/tests/testthat/_snaps/theme/axes-styling.svg b/tests/testthat/_snaps/theme/axes-styling.svg
index 19febe3af7..12c4dfa9c8 100644
--- a/tests/testthat/_snaps/theme/axes-styling.svg
+++ b/tests/testthat/_snaps/theme/axes-styling.svg
@@ -56,14 +56,14 @@
-2.5
-5.0
-7.5
-10.0
-
-
-
-
+2.5
+5.0
+7.5
+10.0
+
+
+
+
2.5
5.0
@@ -74,14 +74,14 @@
-
-
-
-
-2.5
-5.0
-7.5
-10.0
+
+
+
+
+2.5
+5.0
+7.5
+10.0
diff --git a/tests/testthat/_snaps/theme/ticks-length.svg b/tests/testthat/_snaps/theme/ticks-length.svg
index 0110a6ca1c..b11bd1f602 100644
--- a/tests/testthat/_snaps/theme/ticks-length.svg
+++ b/tests/testthat/_snaps/theme/ticks-length.svg
@@ -40,14 +40,14 @@
-2.5
-5.0
-7.5
-10.0
-
-
-
-
+2.5
+5.0
+7.5
+10.0
+
+
+
+
2.5
5.0
7.5
@@ -56,14 +56,14 @@
-
-
-
-
-2.5
-5.0
-7.5
-10.0
+
+
+
+
+2.5
+5.0
+7.5
+10.0