How to make squares on each side of the triangle without hard-coding the squares?

182 views Asked by At

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?

1

There are 1 answers

0
Allan Cameron On BEST ANSWER

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