Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Could gridtext implement xDetails / yDetails methods? #33

Open
teunbrand opened this issue Jan 13, 2024 · 3 comments · May be fixed by #34
Open

Could gridtext implement xDetails / yDetails methods? #33

teunbrand opened this issue Jan 13, 2024 · 3 comments · May be fixed by #34

Comments

@teunbrand
Copy link

The larger context of this feature request is that I was playing around trying to implement slowkow/ggrepel#169, but ran into the following barrier.

The grobX() and grobY() functions can be used to find locations on the boundary of a grob.
These rely on the xDetails() and yDetails() methods of a grob to report metrics.

An example to find such points for a textGrob() is given below:

library(grid)
library(gridtext)

theta <- seq(0, 315, by = 15)

label <- "Lorem ipsum dolor sit amet, consectetur 
adipiscing elit. In hendrerit metus quam, 
sed rutrum nulla feugiat consequat. Sed eu 
pulvinar purus, vel placerat metus."

txt <- textGrob(label, rot = 45)

grid.newpage()
grid.draw(txt)

x <- grobX(txt, theta)
y <- grobY(txt, theta)

grid.points(x = x, y = y, gp = gpar(col = 'red'))

Attempting to do the same for richtext_grob(), we find that the results are all in the center of the plot.
This is the outcome of the default methods of xDetails() and yDetails().

# With <br> instead of newlines
label <- "Lorem ipsum dolor sit amet, consectetur<br> 
adipiscing elit. In hendrerit metus quam,<br> 
sed rutrum nulla feugiat consequat. Sed eu<br>
pulvinar purus, vel placerat metus."

richtxt <- richtext_grob(label, rot = 45)

grid.newpage()
grid.draw(richtxt)

x <- grobX(richtxt, theta)
y <- grobY(richtxt, theta)

grid.points(x = x, y = y, gp = gpar(col = 'red'))

We can compute the vertices of the enclosing box of a rich text grob quite easily, due to the xext and yext variables the grobs have. If we trick grid into treating these vertices as points and implement the xDetails() and yDetails() methods, we can get grobX() and grobY() methods to work.

as_points <- function(x) {
  grobs <- x$children
  
  if (isTRUE(x$debug)) {
    grobs <- grobs[c(-1, -length(grobs))]
  }
  
  x <- do.call(unit.c, lapply(grobs, function(x) x$x))
  y <- do.call(unit.c, lapply(grobs, function(x) x$y))
  
  xext <- lapply(grobs, function(x) x$xext)
  yext <- lapply(grobs, function(x) x$yext)
  
  x <- rep(x, lengths(xext))
  y <- rep(y, lengths(yext))
  
  structure(
    list(
      x = x + unit(unlist(xext), "pt"), 
      y = y + unit(unlist(yext), "pt")
    ), 
    class = "points"
  )
}

xDetails.richtext_grob <- function(x, theta) {
  xDetails(as_points(x), theta)
}

yDetails.richtext_grob <- function(x, theta) {
  yDetails(as_points(x), theta)
}

grid.newpage()
grid.draw(richtxt)

x <- grobX(richtxt, theta)
y <- grobY(richtxt, theta)

grid.points(x = x, y = y, gp = gpar(col = 'red'))

Created on 2024-01-13 with reprex v2.0.2

Please note that the image above is not identical to the textGrob(), which I think is due to the treatment of the lineheight parameter.

So the ask here is if it is possible to implement these methods in {gridtext}? I can prepare a PR with the approach above if we're all in agreement.

@bwiernik
Copy link
Collaborator

That looks reasonable. A PR would be great!

@teunbrand teunbrand linked a pull request Jan 13, 2024 that will close this issue
@clauswilke
Copy link
Collaborator

Sure, no objection from my end, as long as you feel confident you know what you're doing. (I haven't touched grid in a while and so don't remember all the details, other than I always felt it was overly complicated and confusing.) If I remember correctly, I've been frustrated in the past that grid doesn't calculate these numbers automatically for compound grobs.

@teunbrand
Copy link
Author

Thanks Brenton and Claus. I definitely share some frustration about grid. I'm reasonably sure that this does the correct thing for the application I have in mind. Given that xDetails(<richtext_grob>) was giving the wrong result for sure, I don't think this'll cause any harm.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants