Skip to content

Commit 7f5b7cb

Browse files
authored
Merge branch 'main' into mass_to_suggests
2 parents 37f02f9 + 926f290 commit 7f5b7cb

12 files changed

+180
-262
lines changed

DESCRIPTION

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,6 @@ Collate:
151151
'geom-density2d.R'
152152
'geom-dotplot.R'
153153
'geom-errorbar.R'
154-
'geom-errorbarh.R'
155154
'geom-freqpoly.R'
156155
'geom-function.R'
157156
'geom-hex.R'

NEWS.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
* `geom_errorbarh()` is deprecated in favour of
44
`geom_errorbar(orientation = "y")` (@teunbrand, #5961).
5+
* `geom_contour()` should be able to recognise a rotated grid of points
6+
(@teunbrand, #4320)
57
* `geom_boxplot()` gains additional arguments to style the colour, linetype and
68
linewidths of the box, whiskers, median line and staples (@teunbrand, #5126)
79
* (internal) Using `after_scale()` in the `Geom*$default_aes()` field is now

R/geom-errorbar.R

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,35 @@ geom_errorbar <- function(mapping = NULL, data = NULL,
2323
)
2424
}
2525

26+
#' @export
27+
#' @rdname geom_linerange
28+
#' @note
29+
#' `geom_errorbarh()` is `r lifecycle::badge("deprecated")`. Use
30+
#' `geom_errorbar(orientation = "y")` instead.
31+
geom_errorbarh <- function(mapping = NULL, data = NULL,
32+
stat = "identity", position = "identity",
33+
...,
34+
orientation = "y",
35+
na.rm = FALSE,
36+
show.legend = NA,
37+
inherit.aes = TRUE) {
38+
deprecate_soft0(
39+
"3.5.2", "geom_errobarh()", "geom_errorbar(orientation = \"y\")",
40+
id = "no-more-errorbarh"
41+
)
42+
geom_errorbar(
43+
mapping = mapping,
44+
data = data,
45+
stat = stat,
46+
position = position,
47+
...,
48+
orientation = orientation,
49+
na.rm = na.rm,
50+
show.legend = show.legend,
51+
inherit.aes = inherit.aes
52+
)
53+
}
54+
2655
#' @rdname ggplot2-ggproto
2756
#' @format NULL
2857
#' @usage NULL
@@ -80,3 +109,18 @@ GeomErrorbar <- ggproto("GeomErrorbar", Geom,
80109

81110
rename_size = TRUE
82111
)
112+
113+
#' @rdname ggplot2-ggproto
114+
#' @format NULL
115+
#' @usage NULL
116+
#' @export
117+
GeomErrorbarh <- ggproto(
118+
"GeomErrorbarh", GeomErrorbar,
119+
setup_params = function(data, params) {
120+
deprecate_soft0(
121+
"3.5.2", "geom_errobarh()", "geom_errorbar(orientation = \"y\")",
122+
id = "no-more-errorbarh"
123+
)
124+
GeomLinerange$setup_params(data, params)
125+
}
126+
)

R/geom-errorbarh.R

Lines changed: 0 additions & 91 deletions
This file was deleted.

R/geom-linerange.R

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,7 @@
1111
#' `geom_pointrange()`.
1212
#' @seealso
1313
#' [stat_summary()] for examples of these guys in use,
14-
#' [geom_smooth()] for continuous analogue,
15-
#' [geom_errorbarh()] for a horizontal error bar.
14+
#' [geom_smooth()] for continuous analogue
1615
#' @export
1716
#' @inheritParams layer
1817
#' @inheritParams geom_bar

R/stat-contour.R

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,9 @@ StatContour <- ggproto("StatContour", Stat,
104104

105105
compute_group = function(data, scales, z.range, bins = NULL, binwidth = NULL,
106106
breaks = NULL, na.rm = FALSE) {
107+
# Undo data rotation
108+
rotation <- estimate_contour_angle(data$x, data$y)
109+
data[c("x", "y")] <- rotate_xy(data$x, data$y, -rotation)
107110

108111
breaks <- contour_breaks(z.range, bins, binwidth, breaks)
109112

@@ -113,6 +116,8 @@ StatContour <- ggproto("StatContour", Stat,
113116
path_df$level <- as.numeric(path_df$level)
114117
path_df$nlevel <- rescale_max(path_df$level)
115118

119+
# Re-apply data rotation
120+
path_df[c("x", "y")] <- rotate_xy(path_df$x, path_df$y, rotation)
116121
path_df
117122
}
118123
)
@@ -138,6 +143,11 @@ StatContourFilled <- ggproto("StatContourFilled", Stat,
138143
},
139144

140145
compute_group = function(data, scales, z.range, bins = NULL, binwidth = NULL, breaks = NULL, na.rm = FALSE) {
146+
147+
# Undo data rotation
148+
rotation <- estimate_contour_angle(data$x, data$y)
149+
data[c("x", "y")] <- rotate_xy(data$x, data$y, -rotation)
150+
141151
breaks <- contour_breaks(z.range, bins, binwidth, breaks)
142152

143153
isobands <- withr::with_options(list(OutDec = "."), xyz_to_isobands(data, breaks))
@@ -149,6 +159,8 @@ StatContourFilled <- ggproto("StatContourFilled", Stat,
149159
path_df$level_high <- breaks[as.numeric(path_df$level) + 1]
150160
path_df$level_mid <- 0.5*(path_df$level_low + path_df$level_high)
151161
path_df$nlevel <- rescale_max(path_df$level_high)
162+
# Re-apply data rotation
163+
path_df[c("x", "y")] <- rotate_xy(path_df$x, path_df$y, rotation)
152164

153165
path_df
154166
}
@@ -356,3 +368,49 @@ contour_deduplicate <- function(data, check = c("x", "y", "group", "PANEL")) {
356368
}
357369
data
358370
}
371+
372+
estimate_contour_angle <- function(x, y) {
373+
374+
# Compute most frequent angle among first 20 points
375+
all_angles <- atan2(diff(head(y, 20L)), diff(head(x, 20L)))
376+
freq <- tabulate(match(all_angles, unique(all_angles)))
377+
i <- which.max(freq)
378+
379+
# If this angle represents less than half of the angles, we probably
380+
# have unordered data, in which case the approach above is invalid
381+
if ((freq[i] / sum(freq)) < 0.5) {
382+
# In such case, try approach with convex hull
383+
hull <- grDevices::chull(x, y)
384+
hull <- c(hull, hull[1])
385+
# Find largest edge along hull
386+
dx <- diff(x[hull])
387+
dy <- diff(y[hull])
388+
i <- which.max(sqrt(dx^2 + dy^2))
389+
# Take angle of largest edge
390+
angle <- atan2(dy[i], dx[i])
391+
} else {
392+
angle <- all_angles[i]
393+
}
394+
395+
# No need to rotate contour data when angle is straight
396+
straight <- abs(angle - c(-1, -0.5, 0, 0.5, 1) * pi) < sqrt(.Machine$double.eps)
397+
if (any(straight)) {
398+
return(0)
399+
}
400+
angle
401+
}
402+
403+
rotate_xy <- function(x, y, angle) {
404+
# Skip rotation if angle was straight
405+
if (angle == 0) {
406+
return(list(x = x, y = y))
407+
}
408+
cos <- cos(angle)
409+
sin <- sin(angle)
410+
# Using zapsmall to make `unique0` later recognise values that may have
411+
# rounding errors.
412+
list(
413+
x = zapsmall(cos * x - sin * y, digits = 13),
414+
y = zapsmall(sin * x + cos * y, digits = 13)
415+
)
416+
}

0 commit comments

Comments
 (0)