99# ' `grob`, no drawing will occur. The input data for the function should
1010# ' include a data frame with the coordinates of the pair of observations to
1111# ' be linked.
12- # ' @inheritParams pair_links
12+ # ' @inheritParams .link_draw
1313# ' @seealso
1414# ' - [`link_line()`]
1515# ' - [`.link_draw()`]
1616# ' @importFrom rlang is_empty inject
17+ # ' @importFrom grid gTree gList
1718# ' @export
1819link_draw <- function (.draw , ... ) {
1920 if (! is.function(draw <- allow_lambda(.draw ))) {
@@ -22,9 +23,7 @@ link_draw <- function(.draw, ...) {
2223 new_draw <- function (data ) {
2324 ans <- lapply(data , draw )
2425 ans <- ans [vapply(ans , is.grob , logical (1L ), USE.NAMES = FALSE )]
25- if (! is_empty(ans )) {
26- grid :: gTree(children = inject(grid :: gList(!!! ans )))
27- }
26+ if (! is_empty(ans )) inject(gList(!!! ans ))
2827 }
2928 .link_draw(new_draw , ... )
3029}
@@ -42,7 +41,7 @@ link_draw <- function(.draw, ...) {
4241# ' a list, where each item is a data frame containing the coordinates of
4342# ' the pair of observations.
4443# '
45- # ' @inheritParams link_draw
44+ # ' @inheritParams pair_links
4645# ' @seealso [`link_draw()`]
4746# ' @export
4847.link_draw <- function (.draw , ... ) {
@@ -64,9 +63,9 @@ print.ggalign_link_draw <- function(x, ...) {
6463 invisible (x )
6564}
6665
67- # ' Link the observations with a line
66+ # ' Link the paired observations with a line
6867# '
69- # ' @inheritParams link_draw
68+ # ' @inheritParams . link_draw
7069# ' @param element A [`element_line()`][ggplot2::element_line] object. Vectorized
7170# ' fields will be recycled to match the total number of groups, or you can
7271# ' wrap the element with [`I()`] to recycle to match the drawing groups. The
@@ -85,17 +84,18 @@ link_line <- function(..., element = NULL) {
8584 }
8685 ans <- .link_draw(.draw = function (data ) {
8786 data <- lapply(data , function (d ) {
87+ # if the link is only in one side, we do nothing
8888 if (vec_unique_count(.subset2(d , " .hand" )) < 2L ) {
8989 return (NULL )
9090 }
9191 both <- .subset2(vec_split(d , .subset2(d , " .hand" )), " val" )
9292 data <- cross_join(.subset2(both , 1L ), .subset2(both , 2L ))
9393 data_frame0(
94- x = vec_c (
94+ x = vec_interleave (
9595 (data $ x.x + data $ xend.x ) / 2L ,
9696 (data $ x.y + data $ xend.y ) / 2L
9797 ),
98- y = vec_c (
98+ y = vec_interleave (
9999 (data $ y.x + data $ yend.x ) / 2L ,
100100 (data $ y.y + data $ yend.y ) / 2L
101101 )
@@ -124,6 +124,78 @@ link_line <- function(..., element = NULL) {
124124 add_class(ans , " ggalign_link_line" )
125125}
126126
127+ # ' Link the paired observations with a quadrilateral
128+ # '
129+ # ' @inheritParams .link_draw
130+ # ' @inheritParams mark_tetragon
131+ # ' @export
132+ link_tetragon <- function (... , element = NULL ) {
133+ assert_s3_class(element , " element_polygon" , allow_null = TRUE )
134+ default <- calc_element(" ggalign.polygon" , complete_theme(theme_get()))
135+ if (is.null(element )) {
136+ element <- default
137+ } else {
138+ element <- ggplot2 :: merge_element(element , default )
139+ }
140+ .link_draw(.draw = function (data ) {
141+ data <- lapply(data , function (d ) {
142+ # if the link is only in one side, we do nothing
143+ if (vec_unique_count(.subset2(d , " .hand" )) < 2L ) {
144+ return (NULL )
145+ }
146+ both <- .subset2(vec_split(d , .subset2(d , " .hand" )), " val" )
147+ both <- lapply(both , function (link ) {
148+ # find the consecutive groups
149+ index <- .subset2(link , " link_index" )
150+ oindex <- order(index )
151+ group <- cumsum(c(0L , diff(index [oindex ])) != 1L )
152+
153+ # restore the order
154+ group <- group [order(oindex )]
155+
156+ # split link into groups
157+ .subset2(vec_split(link , group ), " val" )
158+ })
159+ both <- vec_expand_grid(
160+ hand1 = .subset2(both , 1L ),
161+ hand2 = .subset2(both , 2L )
162+ )
163+ ans <- .mapply(function (hand1 , hand2 ) {
164+ data_frame0(
165+ x = vec_c(
166+ min(hand1 $ x ), max(hand1 $ xend ),
167+ max(hand2 $ xend ), min(hand2 $ x )
168+ ),
169+ y = vec_c(
170+ min(hand1 $ y ), max(hand1 $ yend ),
171+ max(hand2 $ yend ), min(hand2 $ y )
172+ )
173+ )
174+ }, both , NULL )
175+ vec_rbind(!!! ans )
176+ })
177+ if (inherits(element , " AsIs" )) {
178+ element <- element_rep_len(element ,
179+ length.out = sum(list_sizes(data )) / 4L
180+ )
181+ } else {
182+ element <- element_rep_len(element , length.out = length(data ))
183+ element <- element_vec_rep_each(element ,
184+ times = list_sizes(data ) / 4L
185+ )
186+ }
187+ data <- vec_rbind(!!! data )
188+ if (vec_size(data )) {
189+ element_grob(
190+ element ,
191+ x = data $ x , y = data $ y ,
192+ id.lengths = vec_rep(4L , vec_size(data ) / 4L ),
193+ default.units = " native"
194+ )
195+ }
196+ }, ... )
197+ }
198+
127199# preDraw:
128200# - makeContext
129201# - pushvpgp
@@ -133,9 +205,9 @@ link_line <- function(..., element = NULL) {
133205# postDraw:
134206# - postDrawDetails: by default, do noting
135207# - popgrobvp
136- # ' @importFrom grid makeContent unit convertHeight convertWidth viewport
208+ # ' @importFrom grid makeContent convertHeight convertWidth gList setChildren
137209# ' @export
138- makeContent.ggalignLinkGrob <- function (x ) {
210+ makeContent.ggalignLinkTree <- function (x ) {
139211 # Grab viewport information
140212 width <- convertWidth(unit(1 , " npc" ), " mm" , valueOnly = TRUE )
141213 height <- convertHeight(unit(1 , " npc" ), " mm" , valueOnly = TRUE )
@@ -232,11 +304,16 @@ makeContent.ggalignLinkGrob <- function(x) {
232304 link
233305 })
234306 }
307+
308+ # hand1 - hand2
235309 data <- .mapply(vec_rbind , coords , NULL )
236310 draw <- .subset2(x , " draw" )
237- if (is.grob(grob <- draw(data ))) {
238- makeContent(grob )
311+ if (is.grob(grob <- draw(data ))) { # wrap single grob to a gList
312+ grob <- gList(grob )
313+ }
314+ if (inherits(grob , " gList" )) {
315+ setChildren(x , grob )
239316 } else {
240- grid :: nullGrob()
317+ x
241318 }
242319}
0 commit comments