6

I'm struggling to understand the interactions for ggplot's axis.text.x: angle, hjust and vjust. Everything I've read works when angle is between 0 and 45, but not for angles > 45 and < 90.

Below is a minimal reproducible example:

library(ggplot2)   
p <- ggplot(mtcars, aes(1000 * mpg, hp)) +
      geom_point()

The first plot with angle = 90 looks as expected,

p + theme(axis.text.x = element_text(angle = 90, hjust = 1, vjust = 0.5))

enter image description here

However, when decreasing the angle by only 1 degree compared with the plot above (from angle = 90 to angle = 89), the axis labels are shifted down.

p + theme(axis.text.x = element_text(angle = 89, hjust = 1, vjust = 0.5))

enter image description here

Any insight appreciated.

Henrik
  • 65,555
  • 14
  • 143
  • 159
KJB
  • 121
  • 6

2 Answers2

2

The reason for this behaviour lies all the way inside an unexported function, ggplot2:::rotate_just(), which can be found if you run debug on ggplot2:::element_grob.element_text -> titleGrob -> title_spec.

This is what its code looks like, in my current package version (ggplot2 3.4.2):

> ggplot2:::rotate_just
function (angle, hjust, vjust) 
{
    angle <- (angle %||% 0)%%360
    if (0 <= angle & angle < 90) {
        hnew <- hjust
        vnew <- vjust
    }
    else if (90 <= angle & angle < 180) {
        hnew <- 1 - vjust
        vnew <- hjust
    }
    else if (180 <= angle & angle < 270) {
        hnew <- 1 - hjust
        vnew <- 1 - vjust
    }
    else if (270 <= angle & angle < 360) {
        hnew <- vjust
        vnew <- 1 - hjust
    }
    list(hjust = hnew, vjust = vnew)
}

In your 1st case, with angle = 90, hjust = 1, vjust = 0.5, the second set of conditions are fulfilled, so the modified values are hjust = 0.5, vjust = 1.

In your 2nd case, with angle = 89, hjust = 1, vjust = 0.5, the first set of conditions are fulfilled, so the values remain unchanged as hjust = 1, vjust = 0.5.

If you want the 2nd case's vertical alignment to mimic that of the first, setting vjust = 1 will do the job. (Though I suppose this is an edge case for theoretical interest, because I can't think of many reasons for one to use angle = 89 instead of 90 in real life...)

Z.Lin
  • 28,055
  • 6
  • 54
  • 94
  • the edge case `angle = 89` was included to show the show the unexpected sharp change in behavior from 90 to 89 the desired angle was 75 - a bit easier to read than 90 and hogs less vertical space your answer helps regardless – KJB Apr 18 '23 at 22:04
  • @KJB one additional suggestion: you can add `debug = TRUE` inside `element_text()` to visualize where the text grobs are being "anchored"; I find it helps when making detailed justification adjustments. :) – Z.Lin Apr 19 '23 at 06:21
0

Try specifying both vjust = 0.5 and hjust = 0.5 then you avoid the problem.

ggplot(mtcars, aes(x = 1000*mpg, y = hp)) +
geom_point() +
theme(
axis.text.x = element_text(angle = 75, vjust = .5, hjust = .5)
)

Result

iouraich
  • 2,944
  • 5
  • 29
  • 40