stefano-meschiari / latex2exp

Use LaTeX in R graphics.
Other
186 stars 10 forks source link

\newline support #11

Open DavisBrian opened 6 years ago

DavisBrian commented 6 years ago

Thanks for the great package it has helped me immensely. One thing I quite haven’t figured out is how to add a \newline or \n in a plot. I frequently have long facet labels and/or legend labels that need both line breaks and superscripting. I noticed in issue #8 and #10 it is suggested to use \n to break the line but this does not appear to actually work as suggested.

caijun commented 6 years ago

I also frequently have the same need of line breaks.

caijun commented 6 years ago

I think this feature is related to the line break(\n) support in plotmath expression, rather than latex2exp itself, as latex2exp just converts the LaTeX into corresponding plotmath expression. From the question answered by Uwe Ligges, who is a member of the R Core,

http://r.789695.n4.nabble.com/newline-in-plotmath-expressions-td795815.html

we know directly using \n in plotmath expression cannot achieve the line break effect. To break the line, two solutions can be considered. One is to break the plotmath expression into two individual lines, as suggested in the R help question. The other is using the atop function.

https://stackoverflow.com/questions/29112697/adding-a-newline-in-a-substitute-expression

kmiddleton commented 5 years ago

Here is a workaround to get TeX() to work with atop(). It's not pretty, but it does work.

Use TeX() to generate the expressions:

> TeX("Top Label^2")
expression(`Top Label^2` = paste("Top Label", phantom()^{
    paste("2")
}, ""))
> TeX("Bottom Label_{sub}")
expression(`Bottom Label_{sub}` = paste("Bottom Label", phantom()[{
    paste("sub")
}]))

And then paste everything enclosed in expression() into expression(atop()):

tibble(x = 1:10, y = rnorm(10)) %>% 
  ggplot(aes(x, y)) +
  geom_point() +
  labs(y = expression(
    atop(`Top Label^2` = paste("Top Label", phantom()^{paste("2")}, ""),
         `Bottom Label_{sub}` = paste("Bottom Label", phantom()[{paste("sub")}]))))

If there were an option for TeX() to not wrap its output in expression() (or a separate function to do so), then I think this approach might work for the expression(atop()) approach to multi-line axis labels.

Is there a way to return Top Label^2 = paste("Top Label", phantom()^{paste("2")}, "") from a function?

jerlich commented 5 years ago

I have used a workaround of using cowplot::draw_label. You can put latex text anywhere you want. but it does require a bit of fiddling to get the position just right.

KJByron commented 4 years ago

also would like to see TeX support new lines and have the option to return text instead of an expression @kmiddleton's solution works well until some of the text has to be passed in as variables

here's a workaround using glue and plotmath

x_top <- "Top~Label^2"
x_bot <- "Bottom~Label[sub]" 
tibble(x = 1:10, y = rnorm(10)) %>% 
  ggplot(aes(x, y)) +
  geom_point() +
  labs(y = parse(text = glue::glue("atop({x_top}, fixed~text~{x_bot})")))

but what if I wanted 3 or more lines of code? - yuck... using TeX would be so much cleaner!

I see there is an output argument in TeX, but it currently doesn't seem to do anything

a1 <- TeX("a", output = "expression")
a2 <- TeX("a", output = "text")
identical(a1, a2)
# [1] TRUE
danote commented 4 years ago

A somewhat easier/straightforward variation on @kmiddleton 's workaround is to use $\\overset{x}{y}$ as in latex2exp's homepage examples.

caimiao0714 commented 3 years ago

A somewhat easier/straightforward variation on @kmiddleton 's workaround is to use $\\overset{x}{y}$ as in latex2exp's homepage examples.

Thank you! This work around works for me, is there a good workaround if I need 2 linebreaks (three lines)?

danote commented 3 years ago

A somewhat easier/straightforward variation on @kmiddleton 's workaround is to use $\\overset{x}{y}$ as in latex2exp's homepage examples.

Thank you! This work around works for me, is there a good workaround if I need 2 linebreaks (three lines)?

I'm glad it was helpful. I've never had to use 3 lines, but perhaps it is possible to string together oversets?

bishun945 commented 2 years ago

A somewhat easier/straightforward variation on @kmiddleton 's workaround is to use $\\overset{x}{y}$ as in latex2exp's homepage examples.

Thank you! This work around works for me, is there a good workaround if I need 2 linebreaks (three lines)?

Try ggplot() + xlab(latex2exp::TeX("$\\overset{\\overset{}{x}}{\\overset{y}{z}}$"))

stefano-meschiari commented 2 years ago

In addition to that, if you need to do more than two lines (or one of the lines contains a "tall" command, e.g. \int, that you want to render in full size), you need to wrap the content of each \overset in \normalsize. For example,

plot(TeX(r"(\overset{\normalsize{First line: $\int_a^b x dx$}}{\overset{\normalsize{Second line: $\prod_{i=1}^\infty A_i$}}{\normalsize{Third line: x}}$})"))

will produce image

A future version of latex2exp will do this automatically when it detects line breaks, but for now, it should work fine.

glocke-senda commented 1 year ago

I wish some things were simpler. Here's a function that splits an arbitrary number of lines, including 1.

set.seed(50389082) # least solution to Archimedes' cattle problem. good to know.
latex_lines <- function(each_line) { # spread each entry in each_line onto a different line using overset, wrapping each line in normalsize
  if ("character" != class(each_line)) {
    stop("latex_lines expects a character vector")
  }
  ret <- paste0("\\normalsize{", each_line[1], "}")
  while(0 != length(each_line <- tail(each_line, n=-1))) {
    ret <- paste0("\\overset{", ret, "}{\\normalsize{", each_line[1],"}}")
  }
  return(ret)
}
tibble(x=1:10) %>% 
  dplyr::mutate(
    y=runif(10, max=x),
    lab0 = paste0("\\overset{\\overset{\\normalsize{$\\chi$ == ", x, "}}{\\normalsize{123}}}{\\normalsize{456}}"),
    lab = latex2exp::TeX(lab0, output = 'character'),
    brute_force = paste0("χ = ", x, "\n123\n456")
  ) %>% 
  dplyr::rowwise() %>% 
  dplyr::mutate(
    lab00 = latex_lines(
      c(paste0("$\\chi$ == ", x),
        123,
        456
      )
    ),
    lab2 = latex2exp::TeX(lab00, output = 'character')
  ) %>% 
  ggplot(aes(x=x, y=y, label=lab2)) +
  geom_point() +
  geom_text(aes(y=y+1), parse=T, hjust=0) ## set parse=F to plot the brute_force label

image

lab1 and lab2 produce identical results. brute_force gives the result I actually want (each line left justified).

Needless to say, hjust=0 isn't producing the desired result of left justifying the three lines. If you know of latex that will left justify the overset, please let me know. Also, the last line is separated a bit extra, which afaict means it's not possible to get a publication quality image using overset. The brute force solution looks best in this case, but if you had non-trivial latex expression there would be no brute force solution. If you really need a publication quality image, separate each line into a different geom and "hard code" the line breaks by calculating a different y coordinate.

Hopefully the actual solution our fearless leaders introduce will respect hjust while it adds lines...

ggrothendieck commented 1 year ago

atop and overset center the text. A left-justified version would be desirable, in addition.