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

Element for repelling text #266

Open
wants to merge 6 commits into
base: master
Choose a base branch
from

Conversation

teunbrand
Copy link

This PR aims to fix #248, per promise here: https://bsky.app/profile/teunbrand.bsky.social/post/3lbkg6j53ec25.

Briefly, this is my proposal for a new theme element: element_text_repel(). This element brings that text repelling goodness to non-geom text.

Allow me to walk through a few examples. First, let's make a synthetic plot that just shows the problem we're aiming to solve. The plot below has 3 text labels (in every direction) that are placed so closely together that they overlap.

devtools::load_all("~/packages/ggrepel/")
#> ℹ Loading ggrepel
#> Loading required package: ggplot2

x <- c(0, 4.9, 5, 5.1, 10)
labels <- c("Lorem ipsum", "dolor amet", "consectetur", "adipiscing", "elit")

p <- ggplot(mapping = aes(x, x, colour = x, shape = labels)) +
  geom_point() +
  scale_x_continuous(breaks = x, labels = labels) +
  scale_y_continuous(breaks = x, labels = labels) +
  scale_colour_viridis_c(breaks = x, labels = labels) +
  guides(x.sec = "axis", y.sec = "axis")
p

When we replace all text labels with element_text_repel(), we see that most of the text is repelled. However, the connection from the text to their points of origin, and text justification, is terrible. The legend doesn't have points of origin, so the text there isn't dodged (and probably doesn't need to be dodged).

p + 
  theme(
    axis.text.x.bottom  = element_text_repel(),
    axis.text.y.left    = element_text_repel(),
    axis.text.x.top     = element_text_repel(),
    axis.text.y.right   = element_text_repel(),
    legend.text         = element_text_repel(),
    legend.title        = element_text_repel(),
    axis.title.x.bottom = element_text_repel(),
    axis.title.y.left   = element_text_repel()
  )

We can fix these horrible line connections by allow for a little bit more space through the margin setting. Also, for the secondary axes, we need to set the position, as it can't be automatically guessed from the context.

p + 
  theme(
    axis.text.x.bottom = element_text_repel(margin = margin(t = 10)),
    axis.text.y.left   = element_text_repel(margin = margin(r = 10)),
    axis.text.x.top    = element_text_repel(margin = margin(b = 10), position = "top"),
    axis.text.y.right  = element_text_repel(margin = margin(l = 10), position = "right"),
    legend.text        = element_text_repel(margin = margin(l = 10), position = "right")
  )

Created on 2024-12-02 with reprex v2.1.1

@slowkow
Copy link
Owner

slowkow commented Dec 4, 2024

AMAZING! 🤩 What a great contribution, thank you so much!!

Once I have a moment to play with this on my end, I will come back and comment here if I can find any examples of weird behavior.

Otherwise, I think we should merge this soon.

Some items to do before merging:

  • Add Teun to DESCRIPTION
  • Add a NEWS item about this, crediting Teun
  • Add one or two examples to the Examples page with this feature

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 this pull request may close these issues.

Feature request / offer: element_text_repel()
2 participants