vignettes/experimental-pattern-hex.Rmd
experimental-pattern-hex.Rmd
hex
patternThe pattern is an attempt to create a structured pattern of hex elements using only geometry elements.
hex
pattern functionAll geometry-based pattern creation functions must:
function(params, boundary_df, aspect_ratio, legend)
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#' Create an array of noise using the 'ambient' package
#'
#' @param params aesthetic parameters passed from the geom e.g. 'pattern_fill',
#' 'pattern_frequency' etc.
#' @param boundary_df is a data.frame of (x, y) coordinates of the boundary of
#" the geom to be filled.
#' @param aspect_ratio this is the best guess of the current aspect ratio of the
#' viewport into which the geometry is being drawn
#' @param legend logical. If the request to create a pattern comes during
#' creation of the legend, then this is TRUE, otherwise FALSE
#'
#' @return a grid grob object containing the pattern
#'
#' @import ambient
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
create_pattern_hex <- function(params, boundary_df, aspect_ratio,
legend = FALSE) {
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Pre-scale + rotate the boundary boundary
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
boundary_df$y <- boundary_df$y / aspect_ratio
boundary_df <- rotate_polygon_df(boundary_df, params$pattern_angle, aspect_ratio)
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# 1. Convert the geometry boundary into an 'sf' object
# 2. Make a hexagonal grid using `sf::st_make_grid()`
# 3. Keep only the hexes within the boundary
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
boundary_sf <- convert_polygon_df_to_polygon_sf(boundary_df)
hex_sfc <- st_make_grid(boundary_sf, cellsize = params$pattern_spacing, square = FALSE, flat_topped = FALSE)
hex_within_area_sf <- st_intersection(hex_sfc, boundary_sf)
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Convert the multipolygon sf object containing hexes into a standard
# polygon_df format.
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
polys <- lapply(
seq_along(hex_within_area_sf),
function(idx) {
mat <- as.matrix(hex_within_area_sf[[idx]])
mat <- head(mat, -1)
cbind(mat, idx)
}
)
polys <- do.call(rbind, polys)
polys <- as.data.frame(polys)
polys <- setNames(polys, c('x', 'y', 'id'))
hex_within_area_df <- polys
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Calculate the linewidth to simulate increases in density.
# convert the 'npc' cellsize into native coordinates, and then set the
# linewidth to be a fraction of that size
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
cell_size <- as.numeric(grid::convertWidth(unit(params$pattern_spacing, 'npc'), 'native'))
lwd <- params$pattern_density * cell_size
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Rotate the pattern into its final position
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
hex_within_area_df <- rotate_polygon_df(hex_within_area_df, -params$pattern_angle, aspect_ratio)
hex_within_area_df$y <- hex_within_area_df$y * aspect_ratio
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# return early if there are no hexes in the area
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
if (is.null(hex_within_area_df) || nrow(hex_within_area_df) == 0) {
return(grid::nullGrob())
}
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Create a polygonGrob all all the hexes and clipped hexes
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
hex_grob <- grid::polygonGrob(
x = unit(hex_within_area_df$x, "npc"),
y = unit(hex_within_area_df$y, "npc"),
id = hex_within_area_df$id,
gp = gpar(
col = scales::alpha(params$pattern_fill, params$pattern_alpha),
fill = NA,
lwd = lwd,
lty = params$pattern_linetype,
lineend = 'square'
)
)
hex_grob
}
{ggpattern}
know that there’s an external pattern function it can useA global option (ggpattern_geometry_funcs
) is a named list which contains grid creating functions to use outside of ggpattern
.
The name used in this list corresponds to the pattern
name used with the geom - in this case we will be using pattern = 'hex'
.
hex
pattern
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Create some data to plot
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
df <- data.frame(
trt = c("a", "b", "c"),
outcome = c(2.3, 1.9, 3.2)
)
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Create a ggplot using this pattern
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
p <- ggplot(df, aes(trt, outcome)) +
geom_col_pattern(
aes(
pattern_fill = trt,
pattern_angle = trt
),
pattern = 'hex',
pattern_spacing = 0.02,
pattern_density = 0.1,
pattern_alpha = 1,
fill = 'white',
colour = NA
) +
theme_bw(15) +
labs(
title = "ggpattern::geom_col_pattern()",
subtitle = "pattern = 'hex'"
) +
theme(legend.position = 'none') +
scale_pattern_angle_discrete(range = c(0, 30)) +
coord_fixed(ratio = 1/2)
p
#> Warning: rotate_polygon_df() is deprecated
#> Warning: rotate_polygon_df() is deprecated
#> Warning: rotate_polygon_df() is deprecated
#> Warning: rotate_polygon_df() is deprecated
#> Warning: rotate_polygon_df() is deprecated
#> Warning in cbind(mat, idx): number of rows of result is not a multiple of vector
#> length (arg 2)
#> Warning: rotate_polygon_df() is deprecated
hex
pattern - thicker linesBecause of the way sf::st_intersection()
works, the boundary of the geom gets inextricably linked into the pattern.
Unlike all other patterns, this means that as the pattern changes, the representation of the boundary also changes.
In this example, the thicker lines for the internal hexes, also mean thicker lines for the rectangular boundary.
I don’t really like how this is different from all other patterns.
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Create a ggplot using this pattern
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
p <- ggplot(df, aes(trt, outcome)) +
geom_col_pattern(
aes(pattern_fill = trt),
pattern = 'hex',
pattern_spacing = 0.02,
pattern_angle = 0,
pattern_density = 0.5,
pattern_alpha = 1,
fill = 'white',
colour = NA
) +
theme_bw(15) +
labs(
title = "ggpattern::geom_col_pattern()",
subtitle = "pattern = 'hex'"
) +
theme(legend.position = 'none') +
coord_fixed(ratio = 1/2)
p
#> Warning: rotate_polygon_df() is deprecated
#> Warning: rotate_polygon_df() is deprecated
#> Warning: rotate_polygon_df() is deprecated
#> Warning: rotate_polygon_df() is deprecated
#> Warning: rotate_polygon_df() is deprecated
#> Warning: rotate_polygon_df() is deprecated