Skip to content

Commit c464aa7

Browse files
committed
first draft
1 parent e16ae8b commit c464aa7

File tree

1 file changed

+95
-0
lines changed

1 file changed

+95
-0
lines changed

R/position-connection.R

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
2+
3+
position_connect <- function(connection = "hv") {
4+
ggproto(
5+
NULL, PositionConnect,
6+
connection = validate_connection(connection)
7+
)
8+
}
9+
10+
PositionConnect <- ggproto(
11+
"PositionConnect", Position,
12+
connection = "hv",
13+
setup_params = function(self, data) {
14+
flipped_aes <- has_flipped_aes(data, ambiguous = TRUE)
15+
connection <- self$connection
16+
if (isTRUE(flipped_aes)) {
17+
connection <- connection[, 2:1]
18+
}
19+
list(flipped_aes = flipped_aes, connection = connection)
20+
},
21+
compute_panel = function(data, params, scales) {
22+
data <- flip_data(data, params$flipped_aes)
23+
data <- dapply(data, "group", build_connection, connection = params$connection)
24+
flip_data(data, params$flipped_aes)
25+
}
26+
)
27+
28+
validate_connection <- function(connection, call = caller_env()) {
29+
if (is.character(connection)) {
30+
check_string(connection)
31+
connection <- switch(
32+
arg_match0(connection, c("hv", "vh", "mid", "linear"), error_call = call),
33+
hv = matrix(c(1, 1, 0, 1), 2, 2),
34+
vh = matrix(c(0, 0, 0, 1), 2, 2),
35+
mid = matrix(c(0.5, 0.5, 0, 1), 2, 2),
36+
linear = matrix(c(0, 1, 0, 1), 2, 2)
37+
)
38+
}
39+
if (!is.matrix(connection) ||
40+
!typeof(connection) %in% c("integer", "double") ||
41+
!identical(dim(connection)[2], 2L)) {
42+
extra <- ""
43+
if (!is.null(dim(connection)[2])) {
44+
extra <- " with {dim(connection)[2]} columns"
45+
}
46+
cli::cli_abort(
47+
paste0("{.arg connection} must be a numeric {.cls matrix} with 2 columns, \\
48+
not {.obj_type_friendly {connection}}", extra, "."),
49+
call = call
50+
)
51+
}
52+
connection
53+
}
54+
55+
build_connection <- function(data, connection) {
56+
57+
n <- nrow(data)
58+
if (n <= 1) {
59+
return(vec_slice(data, 0))
60+
}
61+
m <- nrow(connection)
62+
63+
# Sort data on `x`
64+
data <- vec_slice(as.data.frame(data), order(data$x %||% data$xmin))
65+
66+
# Extract x and y aesthetics
67+
x <- as.matrix(data[intersect(names(data), ggplot_global$x_aes)])
68+
y <- as.matrix(data[intersect(names(data), ggplot_global$y_aes)])
69+
70+
# Setup repeats
71+
before <- rep(seq_len(n - 1), each = m)
72+
after <- rep(seq_len(n)[-1], each = m)
73+
xjust <- rep(connection[, 1], n - 1L)
74+
yjust <- rep(connection[, 2], n - 1L)
75+
76+
# Do interpolation
77+
# Note: length(xjust) != length(x). These are kept in sync because the
78+
# matrix recycling rules effectively do `rep(xjust, ncol(x))`.
79+
x <- vec_slice(x, before) * (1 - xjust) + vec_slice(x, after) * xjust
80+
y <- vec_slice(y, before) * (1 - yjust) + vec_slice(y, after) * yjust
81+
82+
# Reconstitute data
83+
new_data <- vec_slice(data, before)
84+
new_data[colnames(x)] <- split_matrix(x)
85+
new_data[colnames(y)] <- split_matrix(y)
86+
87+
# Ensure data starts and ends are intact
88+
if (!all(connection[1, ] == c(0, 0))) {
89+
new_data <- vec_c(vec_slice(data, 1), new_data)
90+
}
91+
if (!all(connection[m, ] == c(1, 1))) {
92+
new_data <- vec_c(new_data, vec_slice(data, n))
93+
}
94+
new_data
95+
}

0 commit comments

Comments
 (0)