-
Notifications
You must be signed in to change notification settings - Fork 18
/
new-scale.R
170 lines (138 loc) · 4.83 KB
/
new-scale.R
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
#' Adds a new scale to a plot
#'
#' Creates a new scale "slot". Geoms added to a plot after this function will
#' use a new scale definition.
#'
#' @param new_aes A string with the name of the aesthetic for which a new scale
#' swill be created.
#'
#' @details
#' `new_scale_color()`, `new_scale_colour()` and `new_scale_fill()` are just
#' aliases to `new_scale("color")`, etc...
#'
#' @examples
#' library(ggplot2)
#'
#' # Equivalent to melt(volcano), but we don't want to depend on reshape2
#' topography <- expand.grid(x = 1:nrow(volcano),
#' y = 1:ncol(volcano))
#' topography$z <- c(volcano)
#'
#' # point measurements of something at a few locations
#' measurements <- data.frame(x = runif(30, 1, 80),
#' y = runif(30, 1, 60),
#' thing = rnorm(30))
#'
#' ggplot(mapping = aes(x, y)) +
#' geom_contour(data = topography, aes(z = z, color = stat(level))) +
#' # Color scale for topography
#' scale_color_viridis_c(option = "D") +
#' # geoms below will use another color scale
#' new_scale_color() +
#' geom_point(data = measurements, size = 3, aes(color = thing)) +
#' # Color scale applied to geoms added after new_scale_color()
#' scale_color_viridis_c(option = "A")
#'
#' @export
new_scale <- function(new_aes) {
structure(ggplot2::standardise_aes_names(new_aes), class = "new_aes")
}
#' @export
#' @rdname new_scale
new_scale_fill <- function() {
new_scale("fill")
}
#' @export
#' @rdname new_scale
new_scale_color <- function() {
new_scale("colour")
}
#' @export
#' @rdname new_scale
new_scale_colour <- function() {
new_scale("colour")
}
#' @export
#' @importFrom ggplot2 ggplot_add
ggplot_add.new_aes <- function(object, plot, object_name) {
plot$layers <- bump_aes_layers(plot$layers, new_aes = object)
plot$scales$scales <- bump_aes_scales(plot$scales$scales, new_aes = object)
plot$labels <- bump_aes_labels(plot$labels, new_aes = object)
plot
}
bump_aes_layers <- function(layers, new_aes) {
lapply(layers, bump_aes_layer, new_aes = new_aes)
}
bump_aes_layer <- function(layer, new_aes) {
original_aes <- new_aes
old_aes <- names(layer$mapping)[remove_new(names(layer$mapping)) %in% new_aes]
new_aes <- paste0(old_aes, "_new")
old_geom <- layer$geom
old_setup <- old_geom$handle_na
new_setup <- function(self, data, params) {
colnames(data)[colnames(data) %in% new_aes] <- original_aes
old_setup(data, params)
}
new_geom <- ggplot2::ggproto(paste0("New", class(old_geom)[1]), old_geom,
handle_na = new_setup)
new_geom$default_aes <- change_name(new_geom$default_aes, old_aes, new_aes)
new_geom$non_missing_aes <- change_name(new_geom$non_missing_aes, old_aes, new_aes)
new_geom$required_aes <- change_name(new_geom$required_aes, old_aes, new_aes)
new_geom$optional_aes <- change_name(new_geom$optional_aes, old_aes, new_aes)
layer$geom <- new_geom
old_stat <- layer$stat
old_setup2 <- old_stat$handle_na
new_setup <- function(self, data, params) {
colnames(data)[colnames(data) %in% new_aes] <- original_aes
old_setup2(data, params)
}
new_stat <- ggplot2::ggproto(paste0("New", class(old_stat)[1]), old_stat,
handle_na = new_setup)
new_stat$default_aes <- change_name(new_stat$default_aes, old_aes, new_aes)
new_stat$non_missing_aes <- change_name(new_stat$non_missing_aes, old_aes, new_aes)
new_stat$required_aes <- change_name(new_stat$required_aes, old_aes, new_aes)
new_stat$optional_aes <- change_name(new_stat$optional_aes, old_aes, new_aes)
layer$stat <- new_stat
layer$mapping <- change_name(layer$mapping, old_aes, new_aes)
layer
}
bump_aes_scales <- function(scales, new_aes) {
lapply(scales, bump_aes_scale, new_aes = new_aes)
}
bump_aes_scale <- function(scale, new_aes) {
old_aes <- scale$aesthetics[remove_new(scale$aesthetics) %in% new_aes]
if (length(old_aes) != 0) {
new_aes <- paste0(old_aes, "_new")
scale$aesthetics[scale$aesthetics %in% old_aes] <- new_aes
if (is.character(scale$guide)) {
scale$guide <- match.fun(paste("guide_", scale$guide, sep = ""))()
}
scale$guide$available_aes[scale$guide$available_aes %in% old_aes] <- new_aes
}
scale
}
bump_aes_labels <- function(labels, new_aes) {
old_aes <- names(labels)[remove_new(names(labels)) %in% new_aes]
new_aes <- paste0(old_aes, "_new")
names(labels)[names(labels) %in% old_aes] <- new_aes
labels
}
change_name <- function(list, old, new) {
UseMethod("change_name")
}
change_name.character <- function(list, old, new) {
list[list %in% old] <- new
list
}
change_name.default <- function(list, old, new) {
nam <- names(list)
nam[nam %in% old] <- new
names(list) <- nam
list
}
change_name.NULL <- function(list, old, new) {
NULL
}
remove_new <- function(aes) {
stringi::stri_replace_all(aes, "", regex = "(_new)*")
}