1

I have a data.frame containing 4 points, which creates a triangle when connected using geom_path:

library(ggplot2)

triangle = data.frame(x = c(0, 0.5, 1, 0),
                      y = c(0, 0.5, 0, 0))

ggplot(triangle, aes(x, y)) +
        geom_path()

enter image description here

Now, I want to create a new data.frame (based on triangle), that has 4 points (e.g. xmin, xmax, ymin, ymax) that creates squares from the sides of the triangle (hence, this data.frame will have 3 rows (for each square) and 4 columns (for each point).

Here is an example:

enter image description here

Is it possible to do it without hard-coding the sides of the squares?

bird
  • 2,938
  • 1
  • 6
  • 27
  • hi, what do you mean by hardcode the sides? eg. fixed input to the xmin, xmax, ymin, ymax arguments? if its a formula with dynamic input does that count as fixed? – alejandro_hagan Apr 16 '22 at 22:04
  • @alejandro_hagan by "hard-coding the sides" I meant giving the numbers (for each point) explicitly. Having a formula that uses the `triangle` `dataframe` to create the `dataframe` for the squares would actually be what I am looking for! :) – bird Apr 16 '22 at 22:07

1 Answers1

4

Since the squares will be at an angle, you probably need the output to be in terms of x, y co-ordinates of the vertices. This just requires a bit of trig. The following function takes a vector of x, y points representing a closed triangle and returns a data frame of the vertices of the squares on each side:

make_squares <- function(x, y) {
  
  x1    <- x[-4]
  x2    <- x[-1]
  xdiff <- (x2 - x1)
    
  y1    <- y[-4]
  y2    <- y[-1]
  ydiff <- (y2 - y1)
  
  lengths <- sqrt(xdiff^2 + ydiff^2)
  angles  <- atan2(ydiff, xdiff)
  
  x3 <- x2 - sin(angles) * lengths
  x4 <- x1 - sin(angles) * lengths
  
  y3 <- y2 + cos(angles) * lengths
  y4 <- y1 + cos(angles) * lengths
  
  
  df <- data.frame(x = round(c(x1, x2, x3, x4, x1), 3),
                   y = round(c(y1, y2, y3, y4, y1), 3),
                   square = rep(1:3, 5))
  `row.names<-`(df[order(df$square),], NULL)
}

The output looks like this:

make_squares(triangle$x, triangle$y)
#>       x    y square
#> 1   0.0  0.0      1
#> 2   0.5  0.5      1
#> 3   0.0  1.0      1
#> 4  -0.5  0.5      1
#> 5   0.0  0.0      1
#> 6   0.5  0.5      2
#> 7   1.0  0.0      2
#> 8   1.5  0.5      2
#> 9   1.0  1.0      2
#> 10  0.5  0.5      2
#> 11  1.0  0.0      3
#> 12  0.0  0.0      3
#> 13  0.0 -1.0      3
#> 14  1.0 -1.0      3
#> 15  1.0  0.0      3

And you can use it in your plot like this:

ggplot(triangle, aes(x, y)) + 
  geom_path() +
  geom_polygon(data = make_squares(triangle$x, triangle$y),
               aes(group = square), fill = "green4", color = "black") +
  coord_equal()

enter image description here

Allan Cameron
  • 147,086
  • 7
  • 49
  • 87