R Markdown

knitr::include_graphics("step0geomcircle.png")
library(ggplot2)

radius <- 10
x0 <- 4
y0 <- 2
angles <- seq(0, 2*pi, length.out = 200)

circle <- data.frame(
  x = cos(angles) * radius + x0,
  y = sin(angles) * radius + y0
  
)

ggplot(circle) + 
  geom_polygon(aes(x, y),
               fill = 'forestgreen',
               colour = 'black') + 
  coord_fixed()

knitr::include_graphics("step1computation.png")

create_circle <- function(data, n){
  
  angles <- seq(from = 0, to = 2*pi,length.out = n+1)
  data.frame()
  
}
knitr::include_graphics("step2ggproto.png")

library(ggplot2)
library(tidyverse)
## ── Attaching core tidyverse packages ─────────────────── tidyverse 2.0.0.9000 ──
## ✔ dplyr     1.1.0     ✔ readr     2.1.4
## ✔ forcats   1.0.0     ✔ stringr   1.5.0
## ✔ lubridate 1.9.2     ✔ tibble    3.2.1
## ✔ purrr     1.0.1     ✔ tidyr     1.3.0
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag()    masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
my_setup <- function(data, params){
  
  if(data$group[1] == -1){
    nrows <- nrow(data)
    data$group <- seq_len(nrows)
  }
  data
}


cars %>% 
  mutate(group = -1L) %>% # specifies 
  my_setup()
##    speed dist group
## 1      4    2     1
## 2      4   10     2
## 3      7    4     3
## 4      7   22     4
## 5      8   16     5
## 6      9   10     6
## 7     10   18     7
## 8     10   26     8
## 9     10   34     9
## 10    11   17    10
## 11    11   28    11
## 12    12   14    12
## 13    12   20    13
## 14    12   24    14
## 15    12   28    15
## 16    13   26    16
## 17    13   34    17
## 18    13   34    18
## 19    13   46    19
## 20    14   26    20
## 21    14   36    21
## 22    14   60    22
## 23    14   80    23
## 24    15   20    24
## 25    15   26    25
## 26    15   54    26
## 27    16   32    27
## 28    16   40    28
## 29    17   32    29
## 30    17   40    30
## 31    17   50    31
## 32    18   42    32
## 33    18   56    33
## 34    18   76    34
## 35    18   84    35
## 36    19   36    36
## 37    19   46    37
## 38    19   68    38
## 39    20   32    39
## 40    20   48    40
## 41    20   52    41
## 42    20   56    42
## 43    20   64    43
## 44    22   66    44
## 45    23   54    45
## 46    24   70    46
## 47    24   92    47
## 48    24   93    48
## 49    24  120    49
## 50    25   85    50
my_setup <- function(data, params)  {
    if (anyDuplicated(data$group)) {
      data$group <- paste(data$group, seq_len(nrow(data)), sep = "-")
    }
    data
  }

cars %>% 
  mutate(group = -1L) %>% 
  my_setup()
##    speed dist group
## 1      4    2  -1-1
## 2      4   10  -1-2
## 3      7    4  -1-3
## 4      7   22  -1-4
## 5      8   16  -1-5
## 6      9   10  -1-6
## 7     10   18  -1-7
## 8     10   26  -1-8
## 9     10   34  -1-9
## 10    11   17 -1-10
## 11    11   28 -1-11
## 12    12   14 -1-12
## 13    12   20 -1-13
## 14    12   24 -1-14
## 15    12   28 -1-15
## 16    13   26 -1-16
## 17    13   34 -1-17
## 18    13   34 -1-18
## 19    13   46 -1-19
## 20    14   26 -1-20
## 21    14   36 -1-21
## 22    14   60 -1-22
## 23    14   80 -1-23
## 24    15   20 -1-24
## 25    15   26 -1-25
## 26    15   54 -1-26
## 27    16   32 -1-27
## 28    16   40 -1-28
## 29    17   32 -1-29
## 30    17   40 -1-30
## 31    17   50 -1-31
## 32    18   42 -1-32
## 33    18   56 -1-33
## 34    18   76 -1-34
## 35    18   84 -1-35
## 36    19   36 -1-36
## 37    19   46 -1-37
## 38    19   68 -1-38
## 39    20   32 -1-39
## 40    20   48 -1-40
## 41    20   52 -1-41
## 42    20   56 -1-42
## 43    20   64 -1-43
## 44    22   66 -1-44
## 45    23   54 -1-45
## 46    24   70 -1-46
## 47    24   92 -1-47
## 48    24   93 -1-48
## 49    24  120 -1-49
## 50    25   85 -1-50
my_compute_panel <- function(data, scales, n = 5, r = 1){
  
  angles <- seq(
    from = 0,
    to = 2*pi,
    length.out = n + 1
  )
  
  nrows = nrow(data)
  
  df <- data.frame() 
  
  for(i in 1:nrows){
    
  bind_rows(df,   
  data.frame(
    x = cos(angles) * data$r[i] + data$x0[i],
    y = cos(angles) * data$r[i] + data$y0[i],
    group = i
  ) 
  ) ->
    df
  }
  
}

cars %>%
  rename(x0 = dist) %>%
  rename(y0 = speed) %>%
  mutate(r = y0/12) %>%
  my_compute_panel


StatCircle <- ggproto(`_class` = 'StatCircle',
                      `_inherit` = Stat, 
                      setup_data = my_setup,
                      compute_panel = my_compute_panel,
                      required_aes = c('x0', 'y0', 'r'))


geom_circle <- function(mapping = NULL, data = NULL, stat = "circle", 
                        position = "identity", ..., r = 1,  
                        n = 50, arrow = NULL, lineend = "butt", linejoin = "round", 
                        na.rm = FALSE, show.legend = NA, inherit.aes = TRUE) {
  layer(
    data = data, 
    mapping = mapping, 
    stat = stat, 
    geom = GeomPolygon, 
    position = position, 
    show.legend = show.legend, 
    inherit.aes = inherit.aes, 
    params = list(
      r = r, 
      n = n, 
      arrow = arrow, 
      lineend = lineend, 
      linejoin = linejoin, 
      na.rm = na.rm, 
      ...
    )
  )
}


ggplot(cars) + 
  aes(x = speed) + 
  aes(y = dist) + 
  geom_point() + 
  aes(r = dist, x0 = speed, y0 = dist) +
  geom_circle()
## Warning in geom_circle(): Ignoring unknown parameters: `arrow`


https://evamaerey.github.io/flipbooks/extending_ggplot2.html#86

library(ggplot2)
library(tidyverse)
create_circle <- function(x0, y0, r = 1, n = 100){
  angels <- seq(
    from = 0,
    to = 2*pi,
    length.out = n + 1
  )
  data.frame(
    x = cos(angels) * r + x0,
    y = sin(angels) * r + y0
  )
}

the_compute_panel <- function(data, scales, r = 1, n = 50) {
    
    cols_to_keep <- setdiff(names(data), c("x0", "y0"))
    
    circles <- lapply(seq_len(nrow(data)), function(i) {
      
      circles_path <- create_circle(data$x0[i], data$y0[i], data$r[i], n)
      cbind(circles_path, unclass(data[i, cols_to_keep]))
      
    })
    
    do.call(rbind, circles)
}

cars %>% 
  .[1:2,] %>% 
  rename(x0 = speed) %>% 
  rename(y0 = dist) %>% 
  mutate(r = y0) %>% 
  mutate(group = row_number()) %>% 
  the_compute_panel(n = 5)
##            x           y  r group
## 1   6.000000  2.00000000  2     1
## 2   4.618034  3.90211303  2     1
## 3   2.381966  3.17557050  2     1
## 4   2.381966  0.82442950  2     1
## 5   4.618034  0.09788697  2     1
## 6   6.000000  2.00000000  2     1
## 7  14.000000 10.00000000 10     2
## 8   7.090170 19.51056516 10     2
## 9  -4.090170 15.87785252 10     2
## 10 -4.090170  4.12214748 10     2
## 11  7.090170  0.48943484 10     2
## 12 14.000000 10.00000000 10     2
the_setup_data <- function(data, params) {
    if (anyDuplicated(data$group)) {
      data$group <- paste(data$group, seq_len(nrow(data)), sep = "-")
    }
    data
  }

cars %>% 
  .[1:2,] %>% 
  mutate(group = row_number()) %>% 
  the_setup_data()
##   speed dist group
## 1     4    2     1
## 2     4   10     2
StatCircle <- ggproto("StatCircle", Stat, 
  # setup_params = function(data, params) {
  #   if (is.null(params$r)) {
  #     params$r <- 1
  #   } else if (params$r == 0) {
  #     rlang::abort("Circles cannot be defined with a radius of 0")
  #   }
  #   if (is.null(params$n)) {
  #     params$n <- 50
  #   } else if (params$n <= 0) {
  #     rlang::abort("Circles must be defined with `n` greater than 0")
  #   }
  #   params
  # },
  setup_data = the_setup_data,
  compute_panel = the_compute_panel,
  required_aes = c("x0", "y0", "r")
)


geom_circle <- function(mapping = NULL, data = NULL, stat = "circle", 
                        position = "identity", ..., r = 1,  
                        n = 50, arrow = NULL, lineend = "butt", linejoin = "round", 
                        na.rm = FALSE, show.legend = NA, inherit.aes = TRUE) {
  layer(
    data = data, 
    mapping = mapping, 
    stat = stat, 
    geom = GeomPolygon, 
    position = position, 
    show.legend = show.legend, 
    inherit.aes = inherit.aes, 
    params = list(
      r = r, 
      n = n, 
      arrow = arrow, 
      lineend = lineend, 
      linejoin = linejoin, 
      na.rm = na.rm, 
      ...
    )
  )
}


set.seed(1244)
cars %>% 
  .[1:5,] %>%
  ggplot() +
  aes(x = speed, y = dist) +
  geom_point() +
  aes(x0 = speed, y0 = dist, r = dist^.5) +
  geom_circle(n = 3, alpha = .2) +
  geom_circle(n = 13, alpha = .2) +
  geom_circle(r = 2, alpha = .2) +
  geom_circle(alpha = .2)
## Warning in geom_circle(n = 3, alpha = 0.2): Ignoring unknown parameters:
## `arrow`
## Warning in geom_circle(n = 13, alpha = 0.2): Ignoring unknown parameters:
## `arrow`
## Warning in geom_circle(r = 2, alpha = 0.2): Ignoring unknown parameters:
## `arrow`
## Warning in geom_circle(alpha = 0.2): Ignoring unknown parameters: `arrow`
## Warning: Duplicated aesthetics after name standardisation: x and y
## Duplicated aesthetics after name standardisation: x and y
## Duplicated aesthetics after name standardisation: x and y
## Duplicated aesthetics after name standardisation: x and y
## Warning in validDetails.polygon(x): NAs introduced by coercion

## Warning in validDetails.polygon(x): NAs introduced by coercion

## Warning in validDetails.polygon(x): NAs introduced by coercion

## Warning in validDetails.polygon(x): NAs introduced by coercion

layer_data(last_plot(), 2)
## Warning: Duplicated aesthetics after name standardisation: x and y
## Warning: Duplicated aesthetics after name standardisation: x and y
## Duplicated aesthetics after name standardisation: x and y
## Duplicated aesthetics after name standardisation: x and y
##            x          y        r x.1 y.1 PANEL group colour   fill linewidth
## 1   5.414214  2.0000000 1.414214   4   2     1  -1-1     NA grey20       0.5
## 2   3.292893  3.2247449 1.414214   4   2     1  -1-1     NA grey20       0.5
## 3   3.292893  0.7752551 1.414214   4   2     1  -1-1     NA grey20       0.5
## 4   5.414214  2.0000000 1.414214   4   2     1  -1-1     NA grey20       0.5
## 5   7.162278 10.0000000 3.162278   4  10     1  -1-2     NA grey20       0.5
## 6   2.418861 12.7386128 3.162278   4  10     1  -1-2     NA grey20       0.5
## 7   2.418861  7.2613872 3.162278   4  10     1  -1-2     NA grey20       0.5
## 8   7.162278 10.0000000 3.162278   4  10     1  -1-2     NA grey20       0.5
## 9   9.000000  4.0000000 2.000000   7   4     1  -1-3     NA grey20       0.5
## 10  6.000000  5.7320508 2.000000   7   4     1  -1-3     NA grey20       0.5
## 11  6.000000  2.2679492 2.000000   7   4     1  -1-3     NA grey20       0.5
## 12  9.000000  4.0000000 2.000000   7   4     1  -1-3     NA grey20       0.5
## 13 11.690416 22.0000000 4.690416   7  22     1  -1-4     NA grey20       0.5
## 14  4.654792 26.0620192 4.690416   7  22     1  -1-4     NA grey20       0.5
## 15  4.654792 17.9379808 4.690416   7  22     1  -1-4     NA grey20       0.5
## 16 11.690416 22.0000000 4.690416   7  22     1  -1-4     NA grey20       0.5
## 17 12.000000 16.0000000 4.000000   8  16     1  -1-5     NA grey20       0.5
## 18  6.000000 19.4641016 4.000000   8  16     1  -1-5     NA grey20       0.5
## 19  6.000000 12.5358984 4.000000   8  16     1  -1-5     NA grey20       0.5
## 20 12.000000 16.0000000 4.000000   8  16     1  -1-5     NA grey20       0.5
##    linetype alpha
## 1         1   0.2
## 2         1   0.2
## 3         1   0.2
## 4         1   0.2
## 5         1   0.2
## 6         1   0.2
## 7         1   0.2
## 8         1   0.2
## 9         1   0.2
## 10        1   0.2
## 11        1   0.2
## 12        1   0.2
## 13        1   0.2
## 14        1   0.2
## 15        1   0.2
## 16        1   0.2
## 17        1   0.2
## 18        1   0.2
## 19        1   0.2
## 20        1   0.2