Dibujar un campo de balonmano con ggplot

The strategy to draw a court is to define the different lines and use ggplot2::geom_path to draw each segment. For circles, we use the following function to create the x-y coordinates.

draw_circle <- function(center = c(0, 0),
                        diameter = 1, 
                        npoints = 12000, 
                        start = 0, 
                        end = 2){
  
  tt <- seq(start*pi, end*pi, length.out = npoints)
  
  data.table::data.table(
    x = center[1] + diameter / 2 * cos(tt),
    y = center[2] + diameter / 2 * sin(tt)
  )

}

Now, we can define each line:

generador_lineas <- function(){
  lineas <- list()
  
  lineas$linea_exterior <- 
    data.table::data.table(x = c(-20, 20, 20, -20, -20),
                           y = c(-10, -10, 10, 10, -10))
  
  lineas$linea_media <- 
    data.table::data.table(x = c(0, 0),
                           y = c(-10, 10))
  
  lineas$marco1 <- 
    data.table::data.table(x = c(-20, -20),
                           y = c(-1.5, 1.5))
  
  lineas$marco2 <- 
    data.table::data.table(x = c(20, 20),
                           y = c(-1.5, 1.5))
  
  
  lineas$linea_6_1 <- 
    data.table::data.table(x = c(-14, -14),
                           y = c(-1.5, 1.5))
  
  lineas$linea_6_2 <- 
    data.table::data.table(x = c(14, 14),
                           y = c(-1.5, 1.5))
  
  lineas$linea_6_1 <- 
    data.table::data.table(x = c(-14, -14),
                           y = c(-1.5, 1.5))
  
  lineas$linea_6_2 <- 
    data.table::data.table(x = c(14, 14),
                           y = c(-1.5, 1.5))
  
  lineas$circulo_6_1_1 <- 
    draw_circle(center = c(-20, -1.5), 
                diameter = 12, 
                start = 1.5, end = 2)
  
  lineas$circulo_6_1_2 <- 
    draw_circle(center = c(-20, 1.5), 
                diameter = 12, 
                start = 0, end = 0.5)
  
  lineas$circulo_6_2_1 <- 
    draw_circle(center = c(20, -1.5), 
                diameter = 12, 
                start = 1, end = 1.5)
  
  lineas$circulo_6_2_2 <- 
    draw_circle(center = c(20, 1.5), 
                diameter = 12,
                start = 0.5, end = 1)
  
  
  lineas$linea_9_1 <- 
    data.table::data.table(x = c(-11, -11),
                           y = c(-1.5, 1.5))
  
  lineas$linea_9_2 <- 
    data.table::data.table(x = c(11, 11),
                           y = c(-1.5, 1.5))
  
  
  lineas$circulo_9_1_1 <- 
    draw_circle(center = c(-20, -1.5),
                diameter = 18, 
                start = 1.62, end = 2)
  
  lineas$circulo_9_1_2 <- 
    draw_circle(center = c(-20, 1.5),
                diameter = 18, 
                start = 0, end = 0.38)
  
  
  lineas$circulo_9_2_1 <- 
    draw_circle(center = c(20, -1.5),
                diameter = 18, 
                start = 1, end = 1.38)
  
  lineas$circulo_9_2_2 <- 
    draw_circle(center = c(20, 1.5), 
                diameter = 18, 
                start = 0.62, end = 1)
  
  
  lineas$linea_portero_1 <-
    data.table::data.table(x = c(-16, -16),
                           y = c(-0.075, 0.075))
  
  lineas$linea_portero_2 <-
    data.table::data.table(x = c(16, 16),
                           y = c(-0.075, 0.075))
  
  lineas$linea_penal_1 <- 
    data.table::data.table(x = c(-13, -13),
                           y = c(-0.5, 0.5))
  
  lineas$linea_penal_2 <-
    data.table::data.table(x = c(13, 13),
                           y = c(-0.5, 0.5))
  
  lineas$sustitucion_1 <- 
    data.table::data.table(x = c(-4.5, -4.5),
                           y = c(-10.15, -9.85))
  
  lineas$sustitucion_2 <- 
    data.table::data.table(x = c(4.5, 4.5),
                           y = c(-10.15, -9.85))
  
  lineas$linea_exterior_media_cancha <- 
    data.table::data.table(x = c(5, 20, 20, 5, 5),
                           y = c(-10, -10, 10, 10, -10))
  
  return(lineas)
}

We are ready to pass these lines to a ggplot for a complete court:

campo <- function(vertical = FALSE, flip = FALSE, plotly = FALSE){
  lineas <- generador_lineas()
  
  make_flip <- NULL
  
  if(flip) make_flip <- '-'
  
  type_of_plot <- 
    data.table::fifelse(vertical,
                        paste0('ggplot2::aes(y, ', make_flip, 'x)'),
                        paste0('ggplot2::aes(', make_flip, 'x, ', 'y)'))
  
  
  plot <- ggplot2::ggplot(mapping = eval(parse(text = type_of_plot))) +
    ggplot2::geom_path(data = lineas$linea_exterior, size = 1) +
    ggplot2::geom_path(data = lineas$linea_media, size = 1) +
    ggplot2::geom_path(data = lineas$marco1, color = 'red', size = 1.5) +
    ggplot2::geom_path(data = lineas$marco2, color = 'red', size = 1.5) +
    ggplot2::geom_path(data = lineas$linea_6_1, size = 1) +
    ggplot2::geom_path(data = lineas$linea_6_2, size = 1) +
    ggplot2::geom_path(data = lineas$circulo_6_1_1, size = 1) +
    ggplot2::geom_path(data = lineas$circulo_6_1_2, size = 1) +
    ggplot2::geom_path(data = lineas$circulo_6_2_1, size = 1) +
    ggplot2::geom_path(data = lineas$circulo_6_2_2, size = 1) +
    ggplot2::geom_path(data = lineas$linea_9_1, linetype = 'dashed', size = 1) +
    ggplot2::geom_path(data = lineas$linea_9_2, linetype = 'dashed', size = 1) +
    ggplot2::geom_path(data = lineas$circulo_9_1_1, linetype = 'dashed', size = 1) +
    ggplot2::geom_path(data = lineas$circulo_9_1_2, linetype = 'dashed', size = 1) +
    ggplot2::geom_path(data = lineas$circulo_9_2_1, linetype = 'dashed', size = 1) +
    ggplot2::geom_path(data = lineas$circulo_9_2_2, linetype = 'dashed', size = 1) +
    ggplot2::geom_path(data = lineas$linea_portero_1, size = 1) +
    ggplot2::geom_path(data = lineas$linea_portero_2, size = 1) +
    ggplot2::geom_path(data = lineas$linea_penal_1, size = 1) +
    ggplot2::geom_path(data = lineas$linea_penal_2, size = 1) +
    ggplot2::geom_path(data = lineas$sustitucion_1, size = 1) +
    ggplot2::geom_path(data = lineas$sustitucion_2, size = 1) +
    ggplot2::coord_fixed() + # We want to maintain the 40x20 proportion 
    ggplot2::theme_void()
  
  if(plotly) plot <- plotly::ggplotly(plot)
  
  return(plot)
}

And half a court:

medio_campo <- function(vertical = TRUE, flip = FALSE, plotly = FALSE){
  lineas <- generador_lineas()
  
  make_flip <- NULL
  
  if(flip) make_flip <- '-'
  
  type_of_plot <- 
    data.table::fifelse(vertical,
                        paste0('ggplot2::aes(y, ', make_flip, 'x)'),
                        paste0('ggplot2::aes(', make_flip, 'x, ', 'y)'))
  
  
  plot <- ggplot2::ggplot(mapping = eval(parse(text = type_of_plot))) +
    ggplot2::geom_path(data = lineas$linea_exterior_media_cancha, size = 1) +
    ggplot2::geom_path(data = lineas$marco2, color = 'red', size = 1.5) +
    ggplot2::geom_path(data = lineas$linea_6_2, size = 1) +
    ggplot2::geom_path(data = lineas$circulo_6_2_1, size = 1) +
    ggplot2::geom_path(data = lineas$circulo_6_2_2, size = 1) +
    ggplot2::geom_path(data = lineas$linea_9_2, linetype = 'dashed', size = 1) +
    ggplot2::geom_path(data = lineas$circulo_9_2_1, linetype = 'dashed', size = 1) +
    ggplot2::geom_path(data = lineas$circulo_9_2_2, linetype = 'dashed', size = 1) +
    ggplot2::geom_path(data = lineas$linea_portero_2, size = 1) +
    ggplot2::geom_path(data = lineas$linea_penal_2, size = 1) +
    ggplot2::coord_fixed() +
    ggplot2::theme_void()
  
  if(plotly) plot <- plotly::ggplotly(plot)
  
  return(plot)
}
campo()

medio_campo(vertical = TRUE, flip = FALSE, plotly = TRUE)