Skip to content

Commit 86dfea0

Browse files
Thomas KnechtThomasKnecht
Thomas Knecht
authored andcommitted
Add position_nudgestack
1 parent 10fa001 commit 86dfea0

File tree

1 file changed

+117
-0
lines changed

1 file changed

+117
-0
lines changed

R/position-nudgestack.R

+117
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
#' Simultaneously nudge and stack
2+
#'
3+
#' This is primarily used for set stacked columns between the ticks on the
4+
#' x-axis.
5+
#'
6+
#' @family position adjustments
7+
#' @param x,y Amount of vertical and horizontal distance to move.
8+
#' @param vjust Vertical adjustment for geoms that have a position
9+
#' (like points or lines), not a dimension (like bars or areas). Set to
10+
#' `0` to align with the bottom, `0.5` for the middle,
11+
#' and `1` (the default) for the top.
12+
#' @param reverse If `TRUE`, will reverse the default stacking order.
13+
#' This is useful if you're rotating both the plot and legend.
14+
#' @export
15+
#' @examples
16+
#' data <- mtcars
17+
#' ggplot() +
18+
#' geom_col(
19+
#' data,
20+
#' aes(x = cyl, y = gear, fill = gear),
21+
#' position = position_nudgestack(x = 1)
22+
#' )
23+
position_nudgestack <- function(x = 0, y = 0, vjust = 1, reverse = FALSE) {
24+
ggproto(NULL, PositionNudgeStack,
25+
x = x,
26+
y = y,
27+
vjust = vjust,
28+
reverse = reverse
29+
)
30+
}
31+
32+
33+
34+
#' @rdname ggplot2-ggproto
35+
#' @format NULL
36+
#' @usage NULL
37+
#' @export
38+
PositionNudgeStack <- ggproto("PositionNudgeStack", Position,
39+
x = 0,
40+
y = 0,
41+
type = NULL,
42+
vjust = 1,
43+
fill = FALSE,
44+
reverse = FALSE,
45+
46+
setup_params = function(self, data) {
47+
list(
48+
x = self$x,
49+
y = self$y,
50+
var = if (!is.null(self$var)) self$var else stack_var(data),
51+
fill = self$fill,
52+
vjust = self$vjust,
53+
reverse = self$reverse
54+
)
55+
},
56+
57+
setup_data = function(self, data, params) {
58+
if (is.null(params$var)) {
59+
return(data)
60+
}
61+
62+
data$ymax <- switch(params$var,
63+
y = data$y,
64+
ymax = ifelse(data$ymax == 0, data$ymin, data$ymax)
65+
)
66+
67+
remove_missing(
68+
data,
69+
vars = c("x", "xmin", "xmax", "y"),
70+
name = "position_stack"
71+
)
72+
},
73+
74+
compute_layer = function(self, data, params, layout) {
75+
if (is.null(params$var)) {
76+
return(data)
77+
}
78+
79+
negative <- data$ymax < 0
80+
negative[is.na(negative)] <- FALSE
81+
82+
neg <- data[negative, , drop = FALSE]
83+
pos <- data[!negative, , drop = FALSE]
84+
85+
if (any(negative)) {
86+
neg <- collide(neg, NULL, "position_stack", pos_stack,
87+
vjust = params$vjust,
88+
fill = params$fill,
89+
reverse = params$reverse
90+
)
91+
}
92+
if (any(!negative)) {
93+
pos <- collide(pos, NULL, "position_stack", pos_stack,
94+
vjust = params$vjust,
95+
fill = params$fill,
96+
reverse = params$reverse
97+
)
98+
}
99+
100+
data <- rbind(neg, pos)[match(seq_len(nrow(data)), c(which(negative), which(!negative))), ]
101+
102+
103+
104+
# transform only the dimensions for which non-zero nudging is requested
105+
if (any(params$x != 0)) {
106+
if (any(params$y != 0)) {
107+
transform_position(data, function(x) x + params$x, function(y) y + params$y)
108+
} else {
109+
transform_position(data, function(x) x + params$x, NULL)
110+
}
111+
} else if (any(params$y != 0)) {
112+
transform_position(data, NULL, function(y) y + params$y)
113+
} else {
114+
data # if both x and y are 0 we don't need to transform
115+
}
116+
}
117+
)

0 commit comments

Comments
 (0)