ingrid provides some tools that I find useful for creating/manipulating grid/grob.
The key benefits I get from ingrid:
row and col - similar to shiny UI definitiongrobs, gTrees and polyclipgrobs
print methods for grobsdefault.unitsig_*() grob creation functions with inline gpar specification
ig_circle(r = .mm(10), fill = 'blue')
igc_*() grob combination operators
igl_*() layout for multiple grobs
igt_*() transform operations on grobs
igt_translate(), igt_rotate()
igt_update() to update any parameter in the grob or viewportig_circle() etc. Analoges for the the grob creation functions in the grid package, but with a slight change in the argument defaults:
gp and vp objects are both created as fully realised structures, and no longer left as NULL if no values are given. This makes manipulation of grob objects a bit easier after they’ve been created.gridGeometry::polyclipgrob() which extends the operations to recurse into gTree and polyclipgrob structures to affect all child objects:
igc_stack() for simple combination of shapesigc_intersect() for shape intersectionigc_union() for shape unionigc_minus() for shape subtractionigc_xor() for shape combination thruogh use of ‘exclusive or’.mm(x) instead of units(x, 'mm')
vp_translate()vp_rotate()gp() is a wrapper around grid::gpar() which is more friendly for IDEs which support auto-complete.print methods with more structured output than grid’s defaultig_pattern() is a wrapper around grid::pattern() where:
extend = 'repeat' is the default (instead of ‘pad’)default.units are in ‘mm’ (instead of ‘npc’)centred = FALSE to disable this behaviour.You can install from GitHub with:
# install.package('remotes')
remotes::install_github('coolbutuseless/ingrid')ingrid reframes the grid layout functions into nested hierarchical layout functions, somewhat similar to how shiny does it’s UI layout.
igl_row() will arrange its sub-elements in a horizontal row.igl_col() will arrange its sub-elements in a vertical column.igl_vp() will create an arbitrary viewport containing its sub-elements.
check <- igl_row(
  igl_col(
    ig_rect(fill = 'black'),
    nullGrob()
  ),
  igl_col(
    nullGrob(),
    ig_rect(fill = 'black')
  )
)
grid.newpage(); grid.draw(check)
check45 <- igl_vp(check, angle = 45)
grid.newpage(); grid.draw(check45)
demo <- igl_row(
  ig_rect  (fill = ig_pattern(check  , width = .cm(1.5), height = .cm(1.5))),
  ig_circle(fill = ig_pattern(check  , width = .cm(2.5), height = .cm(2.5))),
  ig_rect  (fill = ig_pattern(check45, width = .cm(3  ), height = .cm(3  )))
)
grid::grid.newpage(); grid::grid.draw(demo)
Grobs may be combined with igc_*() functions.
igc_stack() combines multiple grobs (as a grobTree()
igc_intersetion(), igc_union(), igc_xor() and igc_minus() are general set operations on grobs using gridGeometry::polyclipGrob()
igc_combine() is the core function used by the above set operatorsigc_mask() and igc_clip() use the new arbitrary clip and mask functionality introduced in R4.1
library(grid)
library(ingrid)
wug <- igc_intersect(
  igc_stack(
    ig_circle(r = .mm(20), fill = 'red', alpha = 0.3),
    ig_circle(r = .mm(33), x = .mid + .mm(25), fill = 'blue', alpha = 0.3),
    ig_circle(r = .mm( 2), x = .mid - .mm(13), y = .mid + .mm(4), fill='black', col='white')
  ),
  ig_circle(r = .mm(28), x = .mid - .mm(19), y = .mid - .mm(20), fill = 'lightblue', col = 'transparent', alpha = 0.3)
)
grid.newpage(); grid.draw(wug)
igc_mask() and igc_clip() use the new arbitrary clip and mask functionality introduced in R4.1
library(grid)
library(ingrid)
# Hex
r     <- .mm(20)
theta <- seq(30, 360, 60) * pi/180 
x     <- .mid + .mm(r * cos(theta))
y     <- .mid + .mm(r * sin(theta))
hex   <- ig_polygon(x, y, fill = 'blue')
# Random raster
ras <- ig_raster(
  image = matrix(runif(100), 10, 10)
)
# Draw individually
grid.newpage()
grid.draw(ras)
grid.draw(hex)
# Mask the raster with the hex
masked <- igc_mask(ras, hex)
grid.newpage(); grid.draw(masked)
# Template object that will be adapted
rect <- ig_rect(width = .cm(2), height = .cm(2), col = 'grey20')
# create grobs
N <- 160
color <- rainbow(N)
grobs <- lapply(seq(N), function(i) {
  igt_translate(
    x = -.mid + .mm(3 * i),
    y = .inch(sin(i/4)),
    igt_rotate(
      angle = i * 2,
      igt_update(rect, fill = color[i])
    )
  )
})
# draw
grobs <- do.call(grid::grobTree, grobs)
grid.newpage(); grid.draw(grobs)
# Template object that will be adapted
rect <- ig_rect(width = .cm(1), height = .cm(1), col = 'white')
N     <- 160
color <- rainbow(N)
# Coords
r     <- .mm(40)
theta <- (seq(0, 360, length.out = N + 1) * pi/180 )[-1]
x     <- .mm(r * cos(theta + 0.2) + r * sin(3 * theta))
y     <- .mm(r * 0.8 * cos(2 * theta) + r * sin(theta))
# create grobs
grobs <- lapply(seq(N), function(i) {
  igt_translate(
    x = x[i],
    y = y[i],
    igt_rotate(
      angle = i * 5,
      igt_update(rect, fill = color[i])
    )
  )
})
# draw
grobs <- do.call(grid::grobTree, grobs)
grid.newpage(); grid.draw(grobs)
ellipseGrob, ngonGrob from gridExtra