Skip to content

Commit d2fb415

Browse files
committed
draft stat_connect()
1 parent 1bfb3c9 commit d2fb415

File tree

1 file changed

+121
-0
lines changed

1 file changed

+121
-0
lines changed

R/stat-connect.R

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
stat_connect <- function(
2+
mapping = NULL,
3+
data = NULL,
4+
geom = "path",
5+
position = "identity",
6+
...,
7+
connection = "hv",
8+
na.rm = FALSE,
9+
show.legend = NA,
10+
inherit.aes = TRUE) {
11+
layer(
12+
data = data,
13+
mapping = mapping,
14+
stat = StatConnect,
15+
geom = geom,
16+
position = position,
17+
show.legend = show.legend,
18+
inherit.aes = inherit.aes,
19+
params = list2(
20+
na.rm = na.rm,
21+
connection = connection,
22+
...
23+
)
24+
)
25+
}
26+
27+
StatConnect <- ggproto(
28+
"StatConnect", Stat,
29+
30+
required_aes = c("x", "y"),
31+
32+
setup_params = function(data, params) {
33+
params$flipped_aes <- has_flipped_aes(data, ambiguous = TRUE)
34+
35+
connection <- params$connection %||% "hv"
36+
37+
if (is.character(connection)) {
38+
check_string(connection)
39+
connection <- switch(
40+
arg_match0(connection, c("hv", "vh", "mid", "linear")),
41+
hv = matrix(c(1, 1, 0, 1), 2, 2),
42+
vh = matrix(c(0, 0, 0, 1), 2, 2),
43+
mid = matrix(c(0.5, 0.5, 0, 1), 2, 2),
44+
linear = matrix(c(0, 1, 0, 1), 2, 2)
45+
)
46+
}
47+
48+
if (!is.matrix(connection) ||
49+
!typeof(connection) %in% c("integer", "double") ||
50+
!identical(dim(connection)[2], 2L)) {
51+
extra <- ""
52+
if (!is.null(dim(connection)[2])) {
53+
extra <- paste0(" with ", dim(connection)[2], " column(s)")
54+
}
55+
cli::cli_abort(
56+
"{.arg connection} must be a numeric {.cls matrix} with 2 columns, \\
57+
not {.obj_type_friendly {connection}}{extra}."
58+
)
59+
}
60+
61+
if (any(!is.finite(connection))) {
62+
cli::cli_abort(
63+
"{.arg connection} cannot contain missing or other non-finite values."
64+
)
65+
}
66+
67+
if (nrow(connection) < 1) {
68+
connection <- NULL
69+
}
70+
71+
params$connection <- connection
72+
params
73+
},
74+
75+
compute_group = function(data, scales, connection = "hv", flipped_aes = FALSE) {
76+
77+
data <- flip_data(data, flipped_aes)
78+
79+
n <- nrow(data)
80+
if (n <= 1) {
81+
return(vec_slice(data, 0))
82+
}
83+
84+
if (!is.matrix(connection)) {
85+
return(data)
86+
}
87+
m <- nrow(connection)
88+
89+
before <- rep(seq_len(n - 1), each = m)
90+
after <- rep(seq_len(n)[-1], each = m)
91+
92+
data <- vec_slice(data, order(data$x %||% data$xmin))
93+
94+
# Interpolate x
95+
# Note that `length(x) != length(xjust)`, but these are kept in sync due to
96+
# the matrix recycling rules (effectively `rep(xjust, ncol(x))`)
97+
x <- as.matrix(data[intersect(names(data), ggplot_global$x_aes)])
98+
xjust <- rep(connection[, 1], n - 1L)
99+
x <- vec_slice(x, before) * (1 - xjust) + vec_slice(x, after) * xjust
100+
101+
# Interpolate y
102+
y <- as.matrix(data[intersect(names(data), ggplot_global$y_aes)])
103+
yjust <- rep(connection[, 2], n - 1L)
104+
y <- vec_slice(y, before) * (1 - yjust) + vec_slice(y, after) * yjust
105+
106+
# Reconstitute data
107+
new_data <- vec_slice(data, before)
108+
new_data[colnames(x)] <- split_matrix(x)
109+
new_data[colnames(y)] <- split_matrix(y)
110+
111+
# Esnure data starts and ends are intact
112+
if (!all(connection[1, ] == c(0, 0))) {
113+
new_data <- vec_c(vec_slice(data, 1), new_data)
114+
}
115+
if (!all(connection[m, ] == c(1, 1))) {
116+
new_data <- vec_c(new_data, vec_slice(data, n))
117+
}
118+
flip_data(new_data, flipped_aes)
119+
}
120+
121+
)

0 commit comments

Comments
 (0)