## ----setup, include = FALSE--------------------------------------------------- knitr::opts_chunk$set(echo = TRUE) library(Gmisc, quietly = TRUE) library(glue) library(htmlTable) library(grid) ## ----flowchart-example, fig.height = 7, fig.width = 8------------------------- grid.newpage() flowchart(source = glue("Stockholm population", "n = {pop}", pop = txtInt(1632798), .sep = "\n"), eligible = glue("Eligible", "n = {pop}", pop = txtInt(10032), .sep = "\n"), included = glue("Randomized", "n = {incl}", incl = txtInt(122), .sep = "\n"), groups = list( glue("Treatment A", "n = {recr}", recr = txtInt(43), .sep = "\n"), glue("Treatment B", "n = {recr}", recr = txtInt(122 - 43 - 30), .sep = "\n") )) |> spread(axis = "y") |> spread(subelement = "groups", axis = "x") |> insert(list(excluded = boxHeaderGrob(header = glue("Excluded (n = {tot}):", tot = 30), body = glue(" - not interested: {uninterested}", " - contra-indicated: {contra}", uninterested = 12, contra = 30 - 12, .sep = "\n"), bjust = "left", header_gp = getOption("boxGrobTxt", default = gpar( color = "black", cex = 1 )))), after = "eligible", name = "excluded") |> move(name = "excluded", x = .8) |> connect("source", "eligible", type = "vert") |> connect("eligible", "included", type = "vert") |> connect("included", "groups", type = "N") |> connect("eligible", "excluded", type = "L", label = "Excluded") ## ----basic_box, fig.height = 1.5, fig.width = 3, message = FALSE-------------- grid.newpage() txt <- "Just a plain box with some text - Note that newline is OK" boxGrob(txt) ## ----styled_box, fig.height = 3, fig.width = 3-------------------------------- grid.newpage() boxGrob("A large\noffset\nyellow\nbox", width = .8, height = .8, x = 0, y = 0, bjust = c("left", "bottom"), txt_gp = gpar(col = "darkblue", cex = 2), box_gp = gpar(fill = "lightyellow", col = "darkblue")) ## ----prop_box, fig.height = 2, fig.width = 4---------------------------------- grid.newpage() boxPropGrob("A box with proportions", "Left side", "Right side", prop = .7) ## ----fig.height = 3, fig.width = 4-------------------------------------------- grid.newpage() smpl_bx <- boxGrob( label = "A simple box", x = .5, y = .9, just = "center") prop_bx <- boxPropGrob( label = "A split box", label_left = "Left side", label_right = "Right side", x = .5, y = .3, prop = .3, just = "center") plot(smpl_bx) plot(prop_bx) smpl_bx_coords <- coords(smpl_bx) grid.circle(y = smpl_bx_coords$y, x = smpl_bx_coords$x, r = unit(2, "mm"), gp = gpar(fill = "#FFFFFF99", col = "black")) grid.circle(y = smpl_bx_coords$bottom, x = smpl_bx_coords$right, r = unit(1, "mm"), gp = gpar(fill = "red")) grid.circle(y = smpl_bx_coords$top, x = smpl_bx_coords$right, r = unit(1, "mm"), gp = gpar(fill = "purple")) grid.circle(y = smpl_bx_coords$bottom, x = smpl_bx_coords$left, r = unit(1, "mm"), gp = gpar(fill = "blue")) grid.circle(y = smpl_bx_coords$top, x = smpl_bx_coords$left, r = unit(1, "mm"), gp = gpar(fill = "orange")) prop_bx_coords <- coords(prop_bx) grid.circle(y = prop_bx_coords$y, x = prop_bx_coords$x, r = unit(2, "mm"), gp = gpar(fill = "#FFFFFF99", col = "black")) grid.circle(y = prop_bx_coords$bottom, x = prop_bx_coords$right_x, r = unit(1, "mm"), gp = gpar(fill = "red")) grid.circle(y = prop_bx_coords$top, x = prop_bx_coords$right_x, r = unit(1, "mm"), gp = gpar(fill = "purple")) grid.circle(y = prop_bx_coords$bottom, x = prop_bx_coords$left_x, r = unit(1, "mm"), gp = gpar(fill = "blue")) grid.circle(y = prop_bx_coords$top, x = prop_bx_coords$left_x, r = unit(1, "mm"), gp = gpar(fill = "orange")) grid.circle(y = prop_bx_coords$bottom, x = prop_bx_coords$right, r = unit(2, "mm"), gp = gpar(fill = "red")) grid.circle(y = prop_bx_coords$top, x = prop_bx_coords$right, r = unit(2, "mm"), gp = gpar(fill = "purple")) grid.circle(y = prop_bx_coords$bottom, x = prop_bx_coords$left, r = unit(2, "mm"), gp = gpar(fill = "blue")) grid.circle(y = prop_bx_coords$top, x = prop_bx_coords$left, r = unit(2, "mm"), gp = gpar(fill = "orange")) ## ----extra_shapes, fig.height = 3, fig.width = 6------------------------------ # --- Branch labels + sharp diamond variant --- grid.newpage() # rounded and sharp diamond examples d_rounded <- boxDiamondGrob("Decision", box_gp = gpar(fill = "#FFF4E6")) d_sharp <- boxDiamondGrob("Decision\n(sharp)", rounded = FALSE, box_gp = gpar(fill = "#FFF4E6")) # outcomes e <- boxEllipseGrob("Local", box_gp = gpar(fill = "#E6FFF4")) r <- boxServerGrob("Server", box_gp = gpar(fill = "#E8F0FF")) # arrange and draw boxes <- list(decision = d_rounded, outcomes = list(e, r)) |> spreadHorizontal(from = unit(.1, "npc"), to = unit(.9, "npc"), subelement = "outcomes") |> spreadVertical() |> print() # 1) quick many-to-many style connector (no labels) con <- connectGrob(boxes$decision, boxes$outcomes, type = "N") print(con) # 2) explicit per-branch connectors with labels (preferred when you want text) connectGrob(boxes$decision, boxes$outcomes[[1]], type = "N", label = "Local") |> print() connectGrob(boxes$decision, boxes$outcomes[[2]], type = "N", label = "Server") |> print() # 3) If you prefer the single connector and want labels on each branch: # place text at the midpoint of each returned grob (example) con_list <- connectGrob(boxes$decision, boxes$outcomes, type = "N") # Preferred: attach labels and let `print()` handle rendering con_list <- setConnectorLabels(con_list, c("Local", "Server")) print(con_list) ## ----standard_shapes, fig.height = 3.5, fig.width = 8------------------------- # Arrange shapes in three rows for better readability # 1) Grid-based objects (basic boxGrob / boxPropGrob / rect) row1 <- list( boxGrob("Box (default)", box_gp = gpar(fill = "#EFEFEF"), y = unit(.85, "npc")), boxPropGrob("Prop", "Left", "Right", prop = .4, box_left_gp = gpar(fill = "#EFEFAF"), box_right_gp = gpar(fill = "#EFAFEF"), y = unit(.85, "npc")), boxGrob("Rectangle", box_fn = rectGrob, box_gp = gpar(fill = "#EFEFEF"), y = unit(.85, "npc")) ) # 2) Gmisc row 1 (rounded/sharp diamond + ellipse + rack + server) row2 <- list( boxDiamondGrob("Diamond\n(rounded)", box_gp = gpar(fill = "#FFF4E6"), y = unit(.55, "npc")), boxDiamondGrob("Diamond\n(sharp)", rounded = FALSE, box_gp = gpar(fill = "#FFF4E6"), y = unit(.55, "npc")), boxEllipseGrob("Ellipse", box_gp = gpar(fill = "#E6FFF4"), y = unit(.55, "npc")), boxRackGrob("Rack", box_gp = gpar(fill = "#E8F0FF"), y = unit(.55, "npc")), boxServerGrob("Server", box_gp = gpar(fill = "#E8F0FF"), y = unit(.55, "npc")) ) # 3) Gmisc row 2 (database, document, documents, tape) row3 <- list( boxDatabaseGrob("Database", box_gp = gpar(fill = "#DFF4E6"), y = unit(.25, "npc")), boxDocumentGrob("Document", box_gp = gpar(fill = "#FFF6E6"), y = unit(.25, "npc")), boxDocumentsGrob("Documents", box_gp = gpar(fill = "#FFF6E6"), y = unit(.25, "npc")), boxTapeGrob("Tape", box_gp = gpar(fill = "#E6F0FF"), y = unit(.25, "npc")) ) # Spread each row across the horizontal span spreadHorizontal(row1, from = unit(.05, "npc"), to = unit(.95, "npc")) spreadHorizontal(row2, from = unit(.05, "npc"), to = unit(.95, "npc")) spreadHorizontal(row3, from = unit(.05, "npc"), to = unit(.95, "npc")) ## ----"Connected boxes", fig.width = 7, fig.height = 5------------------------- grid.newpage() # Initiate the boxes that we want to connect side <- boxPropGrob("Side", "Left", "Right", prop = .3, x = 0, y = .9, bjust = c(0,1)) start <- boxGrob("Top", x = .6, y = coords(side)$y, box_gp = gpar(fill = "yellow")) bottom <- boxGrob("Bottom", x = .6, y = 0, bjust = "bottom") sub_side_left <- boxGrob("Left", x = coords(side)$left_x, y = 0, bjust = "bottom") sub_side_right <- boxGrob("Right", x = coords(side)$right_x, y = 0, bjust = "bottom") odd <- boxGrob("Odd\nbox", x = coords(side)$right, y = .5) odd2 <- boxGrob("Also odd", x = coords(odd)$right + distance(bottom, odd, type = "h", half = TRUE) - unit(2, "mm"), y = 0, bjust = c(1,0)) exclude <- boxGrob("Exclude:\n - Too sick\n - Prev. surgery", x = 1, y = coords(bottom)$top + distance(start, bottom, type = "v", half = TRUE), just = "left", bjust = "right") # Connect the boxes and print/plot them connectGrob(start, bottom, "vertical") connectGrob(start, side, "horizontal") connectGrob(bottom, odd, "Z", "l") connectGrob(odd, odd2, "N", "l") connectGrob(side, sub_side_left, "v", "l") connectGrob(side, sub_side_right, "v", "r") connectGrob(start, exclude, "-", lty_gp = gpar(lwd = 2, col = "darkred", fill = "darkred")) # Print the grobs start bottom side exclude sub_side_left sub_side_right odd odd2 ## ----connect_multi, fig.width = 4, fig.height = 4----------------------------- grid.newpage() # Three upstream boxes + one side box a_boxes <- paste("A", 1:3) |> lapply(\(x) boxGrob(x, box_gp = gpar(fill = "#E6F2FF"))) |> spreadHorizontal(from = unit(.1, "npc"), to = unit(1, "npc") - unit(1, "cm")) |> alignVertical(position="top", reference = unit(1, "npc")) |> print() b_side <- boxGrob("B", y = .70, box_gp = gpar(fill = "#FFF3BF")) |> moveBox(x = unit(1, "npc"), just = 1) |> print() # Target box c <- boxGrob("C", x = .50, box_gp = gpar(fill = "#D3F9D8"), width = unit(4, "cm")) |> moveBox(y = unit(0, "npc"), just = "bottom") |> print() # Many -> one: merge on top with evenly distributed attachment points + margin connectGrob(c(a_boxes, list(b_side)), c, type = "fan_in_top", margin = 4) ## ----horizontal_alignment, fig.width=10, fig.height=6------------------------- align_1 <- boxGrob("Align 1", y = .9, x = 0, bjust = c(0), box_gp = gpar(fill = "#E6E8EF")) align_2 <- boxPropGrob("Align 2", "Placebo", "Treatment", prop = .7, y = .8, x = .5) align_3 <- boxGrob("Align 3\nvertical\ntext", y = 1, x = 1, bjust = c(1, 1), box_gp = gpar(fill = "#E6E8EF")) b1 <- boxGrob("B1", y = .3, x = .1, bjust = c(0)) b2 <- boxGrob("B2 with long\ndescription", y = .6, x = .5) b3 <- boxGrob("B3", y = .2, x = .8, bjust = c(0, 1)) grid.newpage() align_1 alignHorizontal(reference = align_1, b1, b2, b3, position = "left") align_2 alignHorizontal(reference = align_2, b1, b2, b3, position = "center", sub_position = "left") alignHorizontal(reference = align_2, b1, b2, b3, position = "left", sub_position = "right") align_3 alignHorizontal(reference = align_3, b1, b2, b3, position = "right") ## ----vertical_alignment, fig.width=10, fig.height=6--------------------------- align_1 <- boxGrob("Align 1\nvertical\ntext", y = 1, x = 1, bjust = c(1, 1), box_gp = gpar(fill = "#E6E8EF")) align_2 <- boxPropGrob("Align 2", "Placebo", "Treatment", prop = .7, y = .5, x = .6) align_3 <- boxGrob("Align 3", y = 0, x = 0, bjust = c(0, 0), box_gp = gpar(fill = "#E6E8EF")) b1 <- boxGrob("B1", y = .3, x = 0.1, bjust = c(0, 0)) b2 <- boxGrob("B2 with long\ndescription", y = .6, x = .3) b3 <- boxGrob("B3", y = .2, x = .85, bjust = c(0, 1)) grid.newpage() align_1 alignVertical(reference = align_1, b1, b2, b3, position = "top") align_2 alignVertical(reference = align_2, b1, b2, b3, position = "center") align_3 alignVertical(reference = align_3, b1, b2, b3, position = "bottom") ## ----horizontal_spread, fig.width = 11, fig.height = 8------------------------ b1 <- boxGrob("B1", y = .85, x = .1, bjust = c(0, 0)) b2 <- boxGrob("B2", y = .65, x = .6) b3 <- boxGrob("B3", y = .45, x = .6) b4 <- boxGrob("B4 with long\ndescription", y = .7, x = .8) from <- boxGrob("from", y = .25, x = .05, box_gp = gpar(fill = "darkgreen"), txt_gp = gpar(col = "white")) to <- boxGrob("to this wide box", y = coords(from)$y, x = .95, bjust = "right", box_gp = gpar(fill = "darkred"), txt_gp = gpar(col = "white")) txtOut <- function(txt, y_top) { grid.text(txt, x = unit(2, "mm"), y = y_top + unit(2, "mm"), just = c("left", "bottom")) grid.lines(y = y_top + unit(1, "mm"), gp = gpar(col = "grey")) } drawRow <- function(label, row_y, spread_args = list()) { row <- alignVertical(reference = row_y, b1, b2, b3, b4, position = "top") txtOut(label, coords(row[[1]])$top) do.call(spreadHorizontal, c(list(row), spread_args)) } rowYs <- unit(c(.93, .76, .59, .42, .25, .12), "npc") grid.newpage() drawRow("Basic (viewport)", rowYs[1]) drawRow("From–to + margin (numeric = npc)", rowYs[2], spread_args = list(from = .2, to = .7, margin = .05)) drawRow("Only to (defaults from = 0)", rowYs[3], spread_args = list(to = .7)) drawRow("Only from (defaults to = 1)", rowYs[4], spread_args = list(from = .2)) # Row 5: Between boxes (box-to-box span) row5_y <- rowYs[5] row5 <- alignVertical(reference = row5_y, b1, b2, b3, b4, position = "top") txtOut("Between boxes", coords(row5[[1]])$top) span <- alignVertical(reference = row5_y, from = from, to = to, position = "top") span spreadHorizontal(row5, .from = span$from, .to = span$to) # Row 6: Reverse box order + center distribution row6_y <- unit(.10, "npc") bottom_from <- moveBox(from, x = coords(to)$right, y = 0, just = c(1, 0)) bottom_to <- moveBox(to, x = coords(from)$left, y = 0, just = c(0, 0)) bottom_from bottom_to row6 <- alignVertical(reference = bottom_from, b1, b2, b3, b4, position = "bottom") txtOut("Reverse box order + center", coords(row6[[4]])$top) spreadHorizontal(row6, from = bottom_from, to = bottom_to, type = "center") ## ----vertical_spread, fig.width=6, fig.height=6------------------------------- b1 <- boxGrob("B1", y = .8, x = 0.1, bjust = c(0, 0)) b2 <- boxGrob("B2 with long\ndescription", y = .5, x = .5) b3 <- boxGrob("B3", y = .2, x = .8) b4 <- boxGrob("B4", y = .7, x = .8) txtOut <- function(txt, refBx) { grid.text(txt, x = coords(refBx)$left - unit(2, "mm"), y = .5, just = c("center", "bottom"), rot = 90) grid.lines(x = coords(refBx)$left - unit(1, "mm"), gp = gpar(col = "grey")) } grid.newpage() txtOut("Basic", b1) alignHorizontal(reference = b1, b1, b2, b3, b4, position = "left") |> spreadVertical() txtOut("From-to", b2) alignHorizontal(reference = b2, b1, b2, b3, b4, position = "left") |> spreadVertical(from = .2, to = .7) txtOut("From-to with center and reverse the box order", b3) alignHorizontal(reference = b3, b1, b2, b3, b4, position = "left") |> spreadVertical(from = .7, to = .2, type = "center") ## ----complex_nested, fig.width = 7, fig.height = 8---------------------------- # Helper function to convert nested structure to grobs make_boxes <- function(x) { if (is.list(x) && !inherits(x, "box_header")) { return(lapply(x, make_boxes)) } if (inherits(x, "box_header")) { return(do.call(boxHeaderGrob, x)) } # Simple text box fallback args <- attr(x, "args") if (is.null(args)) return(boxGrob(label = x)) args$label <- x do.call(boxGrob, args) } # Define styling for different elements arm_a_style <- list( header = gpar(fill = "#E8F5E9", col = "#2E7D32", lwd = 1.4), box = gpar(fill = "#F1F8E9", col = "#43A047") ) arm_b_style <- list( header = gpar(fill = "#FFF8E1", col = "#EF6C00", lwd = 1.4), box = gpar(fill = "#FFFDE7", col = "#F9A825") ) # Build flowchart structure flowchart <- list( # Shared inclusion criteria criteria = structure( list( header = "Inclusion Criteria", body = paste( "• Adults aged 18-65", "• Confirmed diagnosis", "• Written informed consent", "• No contraindications", "• Available for 6-month follow-up", sep = "\n" ), box_gp = gpar(fill = "#E3F2FD", col = "#1E88E5", lwd = 1.4), body_gp = gpar(fontsize = 10) ), class = "box_header" ), # Two treatment arms arms = list( arm_a = list( # Arm header structure("Intensive Protocol", args = list( box_gp = arm_a_style$header, txt_gp = gpar(fontsize = 11, fontface = "bold") )), # Timeline boxes structure(list( header = "Week 0-1", body = "• Daily sessions\n• Supervised therapy\n", box_gp = arm_a_style$box, body_gp = gpar(fontsize = 9.5) ), class = "box_header"), structure(list( header = "Week 2-4", body = "• 3× weekly sessions\n• Progressive loading", box_gp = arm_a_style$box, body_gp = gpar(fontsize = 9.5) ), class = "box_header"), structure(list( header = "Week 5-8", body = "• Home program\n• Monthly check-ins\n• Return to activity", box_gp = arm_a_style$box, body_gp = gpar(fontsize = 9.5) ), class = "box_header") ), arm_b = list( # Arm header structure("Standard Care", args = list( box_gp = arm_b_style$header, txt_gp = gpar(fontsize = 11, fontface = "bold") )), # Timeline boxes - different schedule structure(list( header = "Month 0", body = "• Initial consultation\n• Exercise booklet", box_gp = arm_b_style$box, body_gp = gpar(fontsize = 9.5) ), class = "box_header"), structure(list( header = "Month 3", body = "• Follow-up visit\n• Progress review", box_gp = arm_b_style$box, body_gp = gpar(fontsize = 9.5) ), class = "box_header"), structure(list( header = "Month 6", body = "• Final assessment\n• Discharge planning", box_gp = arm_b_style$box, body_gp = gpar(fontsize = 9.5) ), class = "box_header") ) ) ) # Convert to grobs and layout grid.newpage() boxes <- flowchart |> make_boxes() |> spreadVertical() |> spreadHorizontal(subelement = "arms", from = 0.15, to = 0.85) |> spreadVertical(subelement = c("arms", "arm_a"), from = 0.65) |> spreadVertical(subelement = c("arms", "arm_b"), from = 0.65) |> print() # Connect criteria to both arms connectGrob(boxes$criteria, boxes$arms, type = "N") # Connect timeline within each arm for (arm_name in names(boxes$arms)) { arm_boxes <- boxes$arms[[arm_name]] for (i in 2:length(arm_boxes)) { connectGrob(arm_boxes[[i-1]], arm_boxes[[i]], type = "v") |> print() } } ## ----s3_api_example, fig.height=8, fig.width=6, eval=FALSE-------------------- # grid.newpage() # # # Define the nodes # b1 <- boxGrob("Start", y = 0.8) # b2 <- boxGrob("Process", y = 0.5) # b3 <- boxGrob("End", y = 0.2) # # # Pipeline: list -> align -> connect -> print # list(start = b1, process = b2, end = b3) |> # align(axis = "y") |> # spread(axis = "x") |> # connect("start", "process", type = "horizontal") |> # connect("process", "end", type = "horizontal") |> # print() ## ----math_expressions, fig.width=6, fig.height=3------------------------------ grid.newpage() ############### # Expressions # ############### # Font style list(expression(bold("Bold text")), expression(italic("Italics text")), expression(paste("Mixed: ", italic("Italics"), " and ", bold("bold")))) |> lapply(boxGrob) |> alignVertical(reference = unit(1, "npc"), position = "top") |> spreadHorizontal() # Math list(expression(paste("y = ", beta[0], " + ", beta[1], X[1], " + ", beta[2], X[2]^2)), expression(paste(hat(mu) == sum(frac(x[i], n), i == 1, n))), expression(paste(int(a, b, f(x) * dx) == F(b) - F(a)))) |> lapply(boxGrob) |> alignVertical(reference = unit(0.5, "npc"), position = "center") |> spreadHorizontal() ########## # Quotes # ########## a = 5 list(bquote(alpha == theta[1] * .(a) + ldots), paste("argument", sQuote("x"), "\nmust be non-zero")) |> lapply(boxGrob) |> alignVertical(reference = unit(0, "npc"), position = "bottom") |> spreadHorizontal(from = .2, to = .8) ## ----basic_plot, fig.height = 2, fig.width = 2-------------------------------- # Load the grid library # part of standard R libraries so no need installing library(grid) # Create a new graph grid.newpage() pushViewport(viewport(width = .5, height = .8)) grid.rect(gp = gpar(fill = "#D8F0D1")) popViewport() ## ----relative_lines, fig.height = 3, fig.width = 3---------------------------- grid.newpage() pushViewport(viewport(width = .5, height = .8, clip = "on")) grid.rect(gp = gpar(lty = 2, fill = "lightyellow")) lg <- linesGrob(x = unit(c(.2, 1), "npc"), y = unit(c(.2, 1), "npc"), gp = gpar(lwd = 2)) grid.draw(lg) pushViewport(viewport(x = 0, y = .6, just = "left", width = .4, height = .4, angle = 20)) grid.rect(gp = gpar(fill = "lightblue")) # A translucent box to indicate the new viewport grid.draw(lg) popViewport() ## ----absolute_lines, fig.height = 3, fig.width = 3---------------------------- grid.newpage() pushViewport(viewport(width = .5, height = .8, clip = "on")) grid.rect(gp = gpar(lty = 2, fill = "lightyellow")) lg <- linesGrob(x = unit(c(2, 10), "mm"), y = unit(c(2, 10), "mm"), gp = gpar(lwd = 2)) grid.draw(lg) pushViewport(viewport(x = 0, y = .6, just = "left", width = .4, height = .4, angle = 20)) grid.rect(gp = gpar(fill = "lightblue")) # A translucent box to indicate the new viewport grid.draw(lg) popViewport() ## ----complex_example, fig.height = 9, fig.width = 9--------------------------- # Define the boxes org_cohort <- glue("Proximal humerus fracture", " - \u2265 18 years", " - \u2264 4 weeks of trauma", " - Not pathological", .sep = "\n") |> boxGrob(just = "left", box_gp = gpar(fill = "#E3F2FD")) surgery <- glue("Surgery", " - Direct (\u2248 4%)", " - Delayed (\u2248 4%)", .sep = "\n") |> boxGrob(just = "left", box_gp = gpar(fill = "#F8BBD0")) randomize <- boxGrob("Non-surgical\nRandomise", box_gp = gpar(fill = "#FFF3E0")) treatments <- list(early = boxGrob("Early rehab", box_gp = gpar(fill = "#DCEDC8")), late = boxGrob("Late rehab", box_gp = gpar(fill = "#DCEDC8")), obs = boxGrob("Observation", box_gp = gpar(fill = "#E0E0E0"))) early_followup <- glue("Early follow-up", " - 2 weeks [PNRS]", " - 4 weeks [PNRS]", .sep = "\n") |> boxGrob(just = "left", box_gp = gpar(fill = "#E0F7FA")) late_followup <- glue("Late follow-up", " - 2-10 months (random) [OSS, PNRS]", " - 1 year [OSS, PNRS, accelerometer]", " - 2 years [OSS, PNRS]", " - 5 years [OSS, PNRS]", .sep = "\n") |> boxGrob(just = "left", box_gp = gpar(fill = "#E0F7FA")) # Create the flowchart grid.newpage() flowchart(start = org_cohort, step_1 = list(surgery = surgery, `non-surgical` = randomize), treatment = treatments, early_followup = early_followup, followup = late_followup) |> spread(axis = "y") |> spread(axis = "x", subelement = "step_1") |> spread(axis = "x", subelement = "treatment", from = 0.35) |> align(axis = "x", reference = c("treatment", "late"), subelement = c("step_1", "non-surgical")) |> connect(from = "start", to = "step_1", type = "N") |> connect(from = "step_1$non-surgical", to = "treatment", type = "N") |> connect(from = "treatment", to = "early_followup", type = "fan_in_center") |> connect(from = "early_followup", to = "followup", type = "v") |> connect(from = "early_followup", to = "step_1$surgery", type = "Z", label = "Crossover\nto surgery") |> connect(from = "step_1$surgery", to = "followup", type = "L") |> print()