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

General Layer Rasteriser #17

Merged
merged 9 commits into from
Jul 20, 2020
Merged

General Layer Rasteriser #17

merged 9 commits into from
Jul 20, 2020

Conversation

teunbrand
Copy link
Contributor

Hi everyone,

TL;DR: The scope of this PR is to add one user facing function rasterise(), which goal is to convert any ggplot2 layer into a layer that produces rasterised output.

However, the only thing rasterise() does is to wrap the draw_panel() method of the input layer to catch the produced grob and attach relevant attributes to the grob. The important bit is that the grob gains an extra class that we can use as a hook to render the grob differently.

The rendering of the grob is done in makeContext.rasteriser(), which is an S3 method for the grid::makeContext() generic. When grid renders graphics it calls makeContext() on the graphical object, which we have overwritten here to have the rasterisation take place.

The main advantage of this strategy over rasterising in the draw_panel() method is that the final dimensions of the plot are known at this step in the drawing process. Therefore we can use these dimensions to get absolute width/height information (rather than relative) about the drawing panel, and thus rasterise the graphical object without distortions (see for example #4).

Here are a couple of features:

  • Under normal conditions plots are returned are similar to the already existing rasterised geoms:
library(ggplot2)
#> Warning: package 'ggplot2' was built under R version 4.0.2
library(ggrastr)

p <- ggplot(diamonds, aes(carat, price, colour = cut))

dpi <- 72 # Since this reprex prints on screen

# Under same dpi output is very comparable
p + rasterise(geom_point(), dpi = dpi) +
  theme(aspect.ratio = 1)

p + geom_point_rast(raster.dpi = dpi) +
  theme(aspect.ratio = 1)

  • When the aspect ratio is distorted, points are still rendered as circles instead of oblong ovals.
# Points remain round across different aspect ratios
p + geom_point_rast(raster.dpi = dpi) +
  theme(aspect.ratio = 0.2)

p + rasterise(geom_point(), dpi = dpi) +
  theme(aspect.ratio = 0.2)

  • Facets are rendered correctly without users having to adjust the width/height settings
# Facets won't warp points
set.seed(0)
p + geom_point_rast(raster.dpi = dpi) +
  facet_wrap(~ sample(1:3, nrow(diamonds), 2))

set.seed(0)
p + rasterise(geom_point(), dpi = dpi) +
  facet_wrap(~ sample(1:3, nrow(diamonds), 2))

  • I also added options to render with the ragg device. It has some differences with cairo. Particularly it can be faster and has better anti-aliasing but the default ragg device also has some alpha blending quirks. Because of these quirks, I also included a ragg_png option to work around the alpha blending.
# Difference in devices are best seen at lower resolution
dpi <- 5
p + rasterise(geom_point(), dpi = dpi, dev = "cairo")

# Default ragg has better anti-aliasing but has unexpected alpha blending
p + rasterise(geom_point(), dpi = dpi, dev = "ragg")

# Ragg png solves the alpha blend, but requires writing a temporary file to
# disk
p + rasterise(geom_point(), dpi = dpi, dev = "ragg_png")

I know it is not good programming etiquette to change things in the description, but I did and because of the following reasons. I imported the grid package, which was already being used in for example geom_point_rast() but I needed ggrastr's namespace to see the makeContext() generic in the grid package. In order for this to happen I needed to add grid to the imports. Also I've included ragg and png to the suggests, as they are not required to run the functions but offer extra functionality. To avoid adding a hard dependency, they've been put into the suggests.

Anyway, I think that is most of the things I should probably explain. Let me know if you have any questions, what you think, particularly if you're okay with the changes I made to the description and whether these functions can find a good fit here in the package. Also feel free to take the function for a spin and review the code yourself.

Best,
Teun

@evanbiederstedt
Copy link
Collaborator

evanbiederstedt commented Jul 7, 2020

Thanks for this

I should have clarified: it's best to create PRs against develop.

Also I've included ragg and png to the suggests, as they are not required to run the functions but offer extra functionality. To avoid adding a hard dependency, they've been put into the suggests.

I actually think it's fine to add them as dependencies (and change the code to reflect this). They're both CRAN packages.

@teunbrand teunbrand changed the base branch from master to develop July 7, 2020 18:48
@teunbrand
Copy link
Contributor Author

Alright, I moved ragg/png from suggests to import and dropped the requireNamespace() checks and switched branch (I think).

@evanbiederstedt
Copy link
Collaborator

Thanks for this @teunbrand

@VPetukhov and I will review the PR within a few days. Naturally, we'll have to update the vignettes as well.

@VPetukhov
Copy link
Owner

Thanks so much, @teunbrand ! This is so elegant, that I need to think more about it :) Sorry for reacting slow.

@teunbrand
Copy link
Contributor Author

Sure, no worries, I'm not in a hurry. Thanks for the kind words. If there are pieces of code that are unclear to you, feel free to ask :)

@VPetukhov
Copy link
Owner

@evanbiederstedt , I'm merging the PR. Could you please then do the changes that we discussed over e-mail?
@teunbrand , thanks so much for the contribution!

@VPetukhov VPetukhov merged commit 813c979 into VPetukhov:develop Jul 20, 2020
@evanbiederstedt
Copy link
Collaborator

Yes, apologies for the delays on this front.

Could you please then do the changes that we discussed over e-mail?

Will do. I'll keep you both informed of the PR.

@VPetukhov
Copy link
Owner

Thanks so much!

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.

3 participants