ggplot2 icon indicating copy to clipboard operation
ggplot2 copied to clipboard

Integrating new grid features in ggplot2

Open thomasp85 opened this issue 5 years ago • 33 comments

Hi Team

The graphic engine and grid is getting some love lately and we will look into a future where gradient + image fill and non-rectangular clipping is available on the devices that supports it. I think both have interest from the ggplot2 side.

While we are not there yet, @pmur002 has a working implementation and I think we may just as well begin discussing to what extend and how we may use this in ggplot2. I think the most interesting (seeing as ggpattern exist) possibility is gradient fill. This will most definitely be a challenge to integrate into the scale system because gradients depend on multiple values. Should the scaling happen from discrete values to a set of predefined gradients? or should each stop be mapped to different values. Lots of things to discuss!

Non-rectangular clipping also opens up a lot of possibilities, some not having a clear parallel in the current API (e.g. clipping groups or layers to certain areas)

@clauswilke @karawoo @Hadley @yutannihilation @paleolimbot please join the discussion 🙂

thomasp85 avatar May 15 '20 07:05 thomasp85

The geologists are going to be so excited! My quick unqualified comment would be that a vectorized "fill spec" (probably generated by ScalePattern$map()) might do the trick. How that interacts with the colour and fill aesthetics seems tricky.

paleolimbot avatar May 15 '20 13:05 paleolimbot

I think allowing fill to vary along x or y in polygons would be very useful, and would probably be something people would expect. As seen here: https://wilkelab.org/ggridges/reference/geom_ridgeline_gradient.html

clauswilke avatar May 15 '20 22:05 clauswilke

If you want to play along, this is all happening in an svn branch [https://svn.r-project.org/R/branches/R-defs/], though that is VERY unstable, both in the sense of being a segfault magnet and in the sense of changing all the time. I am also documenting progress [https://www.stat.auckland.ac.nz/~paul/Reports/r-defs-proposal/defs-proposal.html], though that also needs updating already.

pmur002 avatar May 18 '20 01:05 pmur002

Paul, this is great! Do you take feature requests? One feature that would be really useful and probably isn't that much work (famous last words!) is some sort of link or anchor. Obviously it'd have to be ignored in raster devices, but it'd be super useful for svg or pdf output.

clauswilke avatar May 18 '20 02:05 clauswilke

I take requests, but there is no guarantee that I will do anything with them :) I have this semester teaching free (thanks to an R Studio donation to UoA), which is giving me time to tackle something a bit larger like this, BUT I still have to scope it so that I have some hope of getting something merged back to trunk before I sink without trace back into teaching in the next semester. Long way of saying that additional requests are almost certainly out of scope this time around.

pmur002 avatar May 18 '20 02:05 pmur002

Thanks @thomasp85 and @pmur002 , could this include (or lead to) general textures, by which I mean any given triangle region could have a fill that is mapped from a bitmap image? Interpolation of the image across the triangle is essentially done at device resolution, with mapping from the triangle corners to the coordinate system of the image. So, I see it as a "fill" across a region that can represent the coordinate system of another data set.

mdsumner avatar May 20 '20 02:05 mdsumner

(I know it's off-topic, so sorry for jumping on here - been trying to find where to "park" this wish)

mdsumner avatar May 20 '20 02:05 mdsumner

The scope of this project does not include textures. Not sure if textures are ever a real possibility - I am leaving some graphics devices behind with the set of features under consideration, but textures might leave all graphics devices behind :) One outside chance is "mesh patterns" [https://www.cairographics.org/manual/cairo-cairo-pattern-t.html#cairo-pattern-create-mesh], which both Cairo and PDF can in theory support, but that's still not textures.

pmur002 avatar May 20 '20 05:05 pmur002

With arbitrary clipping regions, it should be relatively straightforward to implement texture fill by just drawing the texture and then clipping to the desired shape. This can be done outside the graphics device, by client code. (Similar to ggpattern but easier to implement.)

clauswilke avatar May 20 '20 06:05 clauswilke

I think what @mdsumner is alluring to is to have a each corner of a triangle mapped to a texture location and let every pixel inside the triangle be interpolated from the texture based on these coordinates... I think this would be too hard to solve generally as well - very few 2D drawing libraries support this

thomasp85 avatar May 20 '20 06:05 thomasp85

Great, thanks for the feedback that alone is helpful - appreciate the work here it's exciting, reading @pmur002's document is very enlightening!

mdsumner avatar May 20 '20 06:05 mdsumner

I forgot that my report is on github, so you can keep up with the latest version here (https://github.com/pmur002/r-defs-proposal/blob/master/defs-proposal.html)

pmur002 avatar May 22 '20 01:05 pmur002

Just linking to relevant discussions and prior work:

  • Write-up of upcoming changes on R dev blog: https://developer.r-project.org/Blog/public/2020/07/15/new-features-in-the-r-graphics-engine/index.html
  • ggpattern package: https://coolbutuseless.github.io/package/ggpattern/index.html
  • ggrough package: https://xvrdm.github.io/ggrough/
  • patternplot package: https://cran.r-project.org/web/packages/patternplot/vignettes/patternplot-intro.html
  • ggtextures package: https://github.com/clauswilke/ggtextures#ggtextures
  • StackExchange question on filling areas with patterns/texture: https://stackoverflow.com/questions/2895319/how-to-add-texture-to-fill-colors-in-ggplot2
    • A custom function that came out of one of the answers: https://github.com/cratlagh/EggHatch/

stragu avatar Aug 06 '20 02:08 stragu

Just wanted to check in and see if there's been any branch work on this. I've been tinkering with {gridtext} a bit to see if element_textbox works with the new gradient support—it does, mostly (I'm having a bit of trouble with PDF support that I'm still working through), but I realised geom_textbox is more likely to just inherit support from ggplot2 when it's working here, so I didn't want to try and reinvent the wheel 😅

jimjam-slam avatar Mar 29 '21 23:03 jimjam-slam

Keen to hear if you have spotted bugs in the PDF support; it would be great to fix those BEFORE 4.1 if possible. You can direct email me if you like.

pmur002 avatar Mar 30 '21 22:03 pmur002

I'll do some more testing and shoot you an email, Paul!

jimjam-slam avatar Mar 31 '21 05:03 jimjam-slam

Great. Thanks

pmur002 avatar Mar 31 '21 20:03 pmur002

False alarm, @pmur002! I was misspecifying the PDF device. This example works for both PDF and PNG on Linux and Windows:

devtools::install_github("rensa/gridtext@feature-gradients")
library(tidyverse)
library(ggtext)

myplot <-
  ggplot(mtcars) +
  aes(mpg, hp) +
  geom_point() +
  theme_grey(base_size = 20) +
  theme(
    plot.title = element_textbox(
      colour = "red",
      fill = grid::linearGradient(
    colours = c("#020024", "#102b81", "#833ab4"),
    stops = c(0, 0.4, 1)))) +
  labs(title = "Hello gradients!", subtitle = "Another go")

ggsave('test_grad_title.png', myplot, device = png(type = "cairo-png"))
ggsave('test_grad_title.pdf', myplot, device = cairo_pdf)

jimjam-slam avatar Apr 04 '21 08:04 jimjam-slam

Ok. Cool. Still good to know that this is working for someone other than me :)
(and those examples also work for me!) Thanks for doing the testing.

pmur002 avatar Apr 05 '21 20:04 pmur002

Just linking to relevant discussions and prior work:

  • Write-up of upcoming changes on R dev blog: https://developer.r-project.org/Blog/public/2020/07/15/new-features-in-the-r-graphics-engine/index.html

  • ggpattern package: https://coolbutuseless.github.io/package/ggpattern/index.html

  • ggrough package: https://xvrdm.github.io/ggrough/

  • patternplot package: https://cran.r-project.org/web/packages/patternplot/vignettes/patternplot-intro.html

  • ggtextures package: https://github.com/clauswilke/ggtextures#ggtextures

  • StackExchange question on filling areas with patterns/texture: https://stackoverflow.com/questions/2895319/how-to-add-texture-to-fill-colors-in-ggplot2

    • A custom function that came out of one of the answers: https://github.com/cratlagh/EggHatch/

The answer in this link contains code that improves ggrough: https://stackoverflow.com/questions/64031046/how-to-shade-shapes

toddrjones avatar Apr 29 '21 18:04 toddrjones

Using patterns in theme elements works fine in ggplot2 3.3.3 with R 4.1, but aesthetics as patterns (even if they're static and not actually mapped to the data) generally stop in check_aesthetics with Aesthetics must be either length 1 or the same as the data (32): fill.

library(ggplot2)
my_gradient <- grid::linearGradient(
  colours = c("#020024", "#102b81", "#833ab4"),
  stops = c(0, 0.4, 1))

# works!
myplot_works <-
  ggplot(mtcars) +
  aes(mpg, hp) +
  geom_point() +
  theme_grey(base_size = 20) +
  theme(
    plot.title = element_textbox(
      colour = "red",
      fill = my_gradient)) +
  labs(title = "Hello gradients!", subtitle = "Another go")
ggsave('test_grad_title.png', myplot_works, device = png(type = "cairo-png"))

# nope
myplot_nope <-
  ggplot(mtcars %>% rownames_to_column()) +
  aes(x = rowname, y = mpg) +
  geom_col(fill = my_gradient) +
  theme_grey(base_size = 20) +
  labs(title = "Hello gradients!", subtitle = "Another go")
ggsave('test_grad_bars.png', myplot_nope, device = png(type = "cairo-png"))

I tried being sneaky and passing a list of patterns for each data element, but it got stuck in farver::decode_colour.

I'm slowly poking away at this in my own time; just wanted to let everyone in know in case it's being worked on privately or an approach has already been sorted! My next step was going to be looking more at how ggplot2 handles aesthetics and maybe doing some vctrs wrappers for the patterns to help handle them explicitly. But, as @thomasp85 noted in the OP, you need to work out how things are handled given a gradient (or, I suppose, a pattern) might depend on multiple data columns.

jimjam-slam avatar May 26 '21 05:05 jimjam-slam

I hope this isn't the wrong place to ask this question, but I've been playing with using linearGradient() for gradient fills based on x/y mappings in {ggdist} and I have a basic implementation working (see the 'Note on “choppy” gradients' here). My question is, is there a way to tell (say in makeContent() or something like that) if the current graphics device supports gradients? That would be helpful as a way to automatically provide fallbacks. Currently I have to provide an option for users to "opt in" to the new linearGradient() approach since I don't know how to detect if it is supported or not.

mjskay avatar Jun 13 '21 06:06 mjskay

Sorry, that information is not currently made available. I should probably add something to the device API, exposed via dev.capabilities(). Unfortunately, that may take a full R version cycle to become available.

pmur002 avatar Jun 13 '21 22:06 pmur002

Makes sense. Thanks for this btw, having proper gradients is really exciting!

mjskay avatar Jun 13 '21 23:06 mjskay

Is this still a good place to watch for updates on work integrating the new gradient features into ggplot? Or for examples in how to use them together?

ssp3nc3r avatar Oct 05 '21 16:10 ssp3nc3r

I would just like to leave this link to Paul Murrell's new report here for people who enjoy seeing what might be in store for R4.2.

teunbrand avatar Nov 18 '21 23:11 teunbrand

@mjskay (and anyone else interested): I have committed a change to r-devel (r81278) for grDevices::dev.capabilities() so that it reports on device support for pattern fills, clipping paths, masks, and some new features just in r-devel. The idea is that FALSE means no support, NA means unknown (but almost certainly no support). For simple cases, TRUE means support, but where support can be more nuanced there is a character vector of supported sub-features. Try, for example ...

xfig()
dev.capabilities()
postscript()
dev.capabilities()
pdf()
dev.capabilities()
png(type="cairo")
dev.capabilities()

Hope that provides the information you need.

pmur002 avatar Dec 01 '21 23:12 pmur002

@pmur002 this is perfect, thank you!

mjskay avatar Dec 02 '21 06:12 mjskay

@mjskay Great. Thanks for taking a look.

pmur002 avatar Dec 03 '21 00:12 pmur002

I don't know if this is the appropriate thread for this, but I recently found out it would be relatively easy to enable pattern fills in ggplot2 (see https://github.com/r-lib/scales/issues/367). However to go from 'enable' to 'full support' might still be some extra work.

teunbrand avatar Oct 31 '22 20:10 teunbrand