Introducing {ingrid} - alternate tools for grab creation, manipulation and output

ingrid

ingrid provides some tools that I find useful for creating/manipulating grid/grob objects interactively in the console.

The name ingrid stands for INteractive GRID

The key benefits I get from ingrid:

  • verbose output of grobs, gTrees and polyclipgrobs, with much more output than their default print methods from {grid}
  • a suite of grob creation functions with inline gpar parameter specification.
  • default.units can be set globally to whatever the user wants, but default to ‘mm’
  • a consistent set of grob combination functions (e.g. intersection, minus) that work across single grobs, gTrees and polyclipgrobs

What’s in the box (Details, details, details)

grid ingrid
grob creation circleGrob(), rectGrob(), etc circle_grob(), rect_grob(), etc
default.units ‘npc’ getOption(‘ingrid.default.units’, ‘mm’)
unit creation unit(10, ‘mm’) .mm(10), .npc(), .points() etc
gpar creation gpar(fill = ‘red’) gp(fill = ‘red’) Has named arguments to support autocompletion.
pattern creation pattern() default extend = ‘pad’ pattern_creation() default extend = ‘repeat’
grob combination gridGeometry::polyclipgrob() grob_stack(), grob_intersect(), grob_union(), grob_minus(), grob_xor() supporting grobs, gTrees, polyclipgrobs
print() print.grob() is terse verbose printing print.grob(), print.gTree(), print.polyclipgrob()
enable verbose printing register_verbose_printing(), deregister_verbose_printing()
grob coord transforms grob_translate(), grob_rotate()
viewport updates vp_translate(), vp_rotate(), vp_setmask(), vp_setclip()
Click here to reveal more details on what’s included
  • grob_circle() etc. Analoges for the the grob creation functions in the grid package, but with a slight change in the argument defaults:
    • default.units are now ‘mm’ instead of ‘npc’
    • the 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.
    • location coordinates (e.g. x, y) are offset from the centre of the viewport by default. Set centred = FALSE to disable this behaviour.
  • Set of functions for combining vanilla grobs, polyclipgrobs and gTree objects. These functions are mostly extensions of gridGeometry::polyclipgrob() which extends the operations to recurse into gTree and polyclipgrob structures to affect all child objects:
    • grob_stack() for simple combination of shapes
    • grob_intersect() for shape intersection
    • grob_union() for shape union
    • grob_minus() for shape subtraction
    • grob_xor() for shape combination thruogh use of ‘exclusive or’
  • Functions for creating units quickly e.g .mm(x) instead of units(x, 'mm')
  • Verbose printing of vanilla grob, gTree and polyclipgrob objects
  • Structured printing of the grid pattern objects available in R4.1.0
  • Simple grob coordinate transformations:
    • grob_translate()
    • grob_rotate()
  • Simple viewport transformations:
    • vp_translate()
    • vp_rotate()
  • gp() is a wrapper around grid::gpar() which is more friendly for IDEs which support auto-complete.
  • Some helpers for patterns:
    • S3 print methods with more structured output than grid’s default
    • pattern_create() is a wrapper around grid::pattern() where:
      • extend = 'repeat' is the default (instead of ‘pad’)
      • default.units are in ‘mm’ (instead of ‘npc’)
      • location coordinates (x, y) are offset from the centre of the viewport by default. Set centred = FALSE to disable this behaviour.

Installation

You can install from GitHub with:

# install.package('remotes')
remotes::install_github('coolbutuseless/ingrid')

Example - verbose grob output

In the code below, circle is created with {grid} and circle2 is created with {ingrid}.

There aren’t any major differences in how this creation works, but look for:

  • default units for ingrid::circle_grob are ‘mm’
  • gpar parameters are given inline with the grob call (although it is also possible to pass in a gp argument)
  • by calling ingrid::register_verbose_printing() the output for circle2 gives much more detail than the default printing.
library(grid)
library(ingrid)

Creating a blue circle in {grid}

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Using 'grid'
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
circle1 <- circleGrob(r = unit(20, 'mm'), gp = gpar(fill = 'lightblue'))
circle1
#> circle[GRID.circle.1]
grid.newpage()
grid.draw(circle1)

Creating a blue circle in {ingrid}

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Using 'ingrid' where default units are 'mm'
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
circle2 <- circle_grob(20, fill = 'lightblue')
circle2
#> circle[GRID.circle.2]
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Enable verbose printing of grobs 
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ingrid::register_verbose_printing()
circle2
#> circle [GRID.circle.2]
#>   x=c(sum(0mm, 0.5npc)), y=c(sum(0mm, 0.5npc)), r=c(20mm)
#>   gp: 
#>     fill: lightblue
#>   vp: [GRID.VP.1] default
grid.newpage()
grid.draw(circle2)

Creating and combining grobs

library(grid)
library(ingrid)
register_verbose_printing()


#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Create 4 circles
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
c1 <- circle_grob(r = 20, fill = 'red', alpha = 0.3)
grid.newpage(); grid.draw(c1);

c2 <- circle_grob(r = 33, x = 25, fill = 'blue', alpha = 0.3)
grid.newpage(); grid.draw(c2);

c3 <- circle_grob(r = 28, x = -19, y = -20, fill = 'lightblue', col = 'transparent', alpha = 0.3)
grid.newpage(); grid.draw(c3);

c4 <- circle_grob(r = 2, x = -13, y= 4, fill='black', col='white')
grid.newpage(); grid.draw(c4);

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Stack some of the circles
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
i1 <- grob_stack(c1, c2, c4)
grid.newpage(); grid.draw(i1);

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Visualise how the 4th circle overlaps
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
grid.newpage();
grid.draw(i1)
grid.draw(c3)

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Keep just the intersection of the stack of 3 circls and the 4th circle
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
wug <- ingrid::grob_intersect(i1, c3)
grid.newpage(); grid.draw(wug);

logo for ingrid

library(grid)
library(ingrid)

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Parameters for a hex
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
r     <- 100
theta <- seq(30, 360, 60) * pi/180 
x     <- r * cos(theta)
y     <- r * sin(theta)

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# a hex polygon
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
hex <- polygon_grob(x, y, fill = 'black')

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Create an image maskeed by the hex
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
jpg <- jpeg::readJPEG("./man/figures/ingrid.jpg")
image <- raster_grob(
  image  = jpg, 
  x      = .mm(-5),
  y      = .mm(-25),
  width  = .mm(2 * 1.0 * r), 
  height = .mm(3 * 1.0 * r),
  mask   = hex
)


#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Position text
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
text <- text_grob(
  label      = 'ingrid',
  x          =  0.25 * r,
  y          = -0.75 * r, 
  rot        = 30,
  just       = 0,
  col        = 'white', 
  fontfamily = 'courier',
  cex        = 4
)

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Draw the objects
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
grid::grid.newpage()
grid::grid.draw(image)
grid::grid.draw(text)

Future

  • Include grobs from other sources: