Q:

Creating exact squares with panel.grid.major in ggplot2

i have the following issue right now;
I want to create plots with ggplot2 where the elements panel.grid.major.x and panel.grid.major.y form squares within the plot.
My solution so far includes defining the amount of major lines from the x- and y-axis of the plot as well as the option aspect.ratio in the theme options. Following code is a MWE, my actual code right now contains more options:
library(ggplot2)
#remotes::install_github("allisonhorst/palmerpenguins")
library(palmerpenguins)

equal_breaks2 <- function(n = 3, s = 0.05, ...){
  function(x){
    # rescaling
    d <- s * diff(range(x)) / (1+2*s)
    seq(min(x)+d, max(x)-d, length=n)
  }
}
# This functions comes from a great answer here
# https://stackoverflow.com/questions/28436855/change-the-number-of-breaks-using-facet-grid-in-ggplot2

n_x <- 5
n_y <- 3

ggplot(palmerpenguins::penguins, aes(x = bill_depth_mm, y= bill_length_mm)) +
  geom_point(aes(colour = species, shape = sex)) +
  scale_color_viridis_d() +
  scale_x_continuous(breaks = equal_breaks2(n = n_x, s = 0.00), expand = c(0,0)) +
  scale_y_continuous(breaks = equal_breaks2(n = n_y, s = 0.00), expand = c(0,0)) +
  theme(aspect.ratio = n_y/n_x,
        panel.grid.minor = element_blank()) +
  coord_fixed()
  

This plot unfortunately does not produce exact squares from the grid lines. One has to manually adjust the aspect ratio (in this example n_y=3.7 looks pretty good).
Does anyone have an idea how to solve this, without having to adjust values manually?
Edit: I forgot to mention this in my initial request; Ideally my plot limits are the min and max value of my breaks, so i also have squares at the borders of the plot.

A:

So, with the great help of @Waldi, i came up with an automatic solution. Its totally viable to do all the calculation beforehand, but i wanted an automatic solution, within the ggplot-chain.
I created my own coord-ggproto object, which can calculate the aspect ratio from the internals in ggplot (According to the Formula of @Waldi).
CoordOwn <- ggproto("CoordOwn", CoordCartesian,
                    is_free = function() FALSE,
                    
                    aspect = function(self, ranges) {
                      d_x = diff(ranges$x.major_source)[1]
                      d_y = diff(ranges$y.major_source)[1]
                      
                      (d_x * diff(ranges$y.range)) / (d_y * diff(ranges$x.range))
                    }
)

coord_own <- function(ratio = 1, xlim = NULL, ylim = NULL, expand = TRUE, clip = "on") {

  ggproto(NULL, CoordOwn,
          limits = list(x = xlim, y = ylim),
          ratio = ratio,
          expand = expand,
          clip = clip
  )

}

Now i can change n_x and n_y however i want them to, and coord_own fixes the aspect ratio accordingly:
n_x <- 5
n_y <- 5

ggplot(palmerpenguins::penguins, aes(x = bill_depth_mm, y= bill_length_mm)) +
  geom_point(aes(colour = species, shape = sex)) +
  scale_color_viridis_d() +
  scale_x_continuous(breaks = equal_breaks2(n = n_x, s = 0.00), expand = c(0,0)) +
  scale_y_continuous(breaks = equal_breaks2(n = n_y, s = 0.00), expand = c(0,0)) +
  theme(panel.grid.minor = element_blank()) +
  coord_own()

