 Hello, and welcome to tonight's keynote session. I'd first like to take a moment to thank our sponsors, Roche and Syncra. I have that right. It is my pleasure to introduce Paul Morell, the keynote speaker for today's session. It is difficult to imagine our graphics without Paul Morell. You may not know it, but if you've ever created a plot using ggplot2, Lattice, VCD, or a host of other high-level packages, your code is relied on the grid graphic system which Paul designed. It is a largely unseen scaffolding supporting so much of our data visualization work in R. The genius of the grid system is its flexibility. Graphics in R is not a list of chart types, it is a complex ecosystem that allows one to build any graph from the ground up. We don't ask, can you make a whatever type of chart in R? We ask, what packages are there to do it? And if we don't find a package that does exactly what we're looking for, we build it. Much of the credit for this infinitely extendable system goes to Paul Morell. It comes as no surprise that the title of his talk tonight is expanding the vocabulary of our graphics. I look forward to hearing the ways in which the boundaries of our graphics are being pushed even further. Please join me in giving a warm virtual welcome to Paul Morell. Thanks to us for that introduction. You've written, you've talked about most of my talk already. So what I'm going to do is talk about some recent ish changes in the graphic system in R and try and give an idea of what that means for what you can do with our graphics. So I'll start with something that is possibly uncontroversial. You may have heard of a public package called GG Plot 2, and it can be used to draw plots in R. What you might not know so well is that GG Plot 2 was just one of the packages that you can use for this sort of thing. So here's a diagram of sort of the graphics ecosystem from my point of view. It seems to feature a bit biased. It features lots of packages that I've written and about 5,000 packages that other people have written missing. So it's sort of from my point of view of the graphics system. But you can see GG Plot 2 in there. And the sort of important feature of this picture is that there's a sort of a black ellipse here that's the heart of the graphics system that I like to call the graphics engine. So I'm representing it here by the GR Devices package, which loads by default when you start it. And the important sort of feature of this picture is that GG Plot 2 actually sort of has to go through this GR Devices package to get to all the different output formats. So in order to see your graphics on screen, GG Plot 2 talks to grid, which talks to the graphics engine and then on out to the screen or to a PDF file or whatever. So essentially that means that the GR Devices package is a bit of a bottleneck. So you can see that all of these different packages for drawing things, they all have to go through GR Devices. Right, now that limits what these packages can do to what GR Devices lets them do. So if you're GG Plot 2, then GR Devices will let you draw rectangles, it will let you draw lines, it will let you draw points, and it will let you draw text. And in lots of cases that's actually all you need and you can produce a plot like GG Plot 2 does. Right, here's another package that draws things in, called River Plot. And I think this is called a Sancti diagram. Now River Plot was already on that picture of the graphics system as well. So it comes down sort of through another low level graphics system, which is an older one called graphics. But it still has to go through GR Devices with the graphics engine as well. So it's also limited to what GR Devices will let it do. This is what River Plot wants to do. This is one of the things that River Plot wants to do. It wants to draw a gradient of color. So we're starting at a green on the left and then transitioning smoothly to a blue on the right. So it has to express that drawing in terms that the graphics engine will understand. So how does it do that? What actually happens is that the graphics engine doesn't understand gradient fields. So what the River Plot package does is it converts that shape into a whole lot of little shapes, little polygons, which the graphics engine does understand, and then fills them all in in slightly different colors heading across to give the impression of a smoothly changing gradient. So wouldn't it be nice if the River Plot package didn't have to work so hard and it could actually just say to the graphics engine, please can you give me a gradient fill that very smoothly? And I'll just give the start color the end color and the graphics engine will do the writing. So you might be able to see what's coming up or at least part of it. So from R410 you can now do things like this. You can create a gradient fill and fill a region with it. Now I'm just showing some code on the next few slides just to make it look like I'm not lying about stuff. I will come back to that code and explain it in more detail later on. I'm just going to make a list of what we can do that's new. So that's one thing. I can make a linear gradient in this case wiped through to transparent. Another thing I can do is I can use a radial gradient fill. So I can in this case start with white in the center and then spreading out slowly to again transparent at the edges. And another new thing I can do is I can fill a region with a pattern. So in this case I'm saying the pattern is a circle and then I say draw something and fill repeat that circle to fill it with a pattern. A slightly fancier thing I can now do as well is I can work with clipping paths. And that means that on the left here I can define a shape and say that that's my clipping path. Then in the middle I will draw something, draw another shape. And the shape that I draw will be clipped to the clipping path. And so only the bits of the shape that are inside the clipping path will actually be seen and then we get the result on the right hand side there. So we've shaved off the corners of that square. And then final new thing that we can do is we can use masks. And the idea of a mask is that on the left here I describe a shape and that has a level of semi-transparency to it. So in this case I've got a semi-transparent white circle. So it looks a little greenish because I can see the background through it. So that's a non-opaque circle. It's a see-through circle. And then the idea is that I can draw another shape once I've got this mask in place. I can draw another shape and then the shape is affected by the mask. So the mask is applied to the shape. And so the result is that the transparency of the mask transfers to the thing that I've drawn. So the bits of the square here that lie inside the mask become semi-transparent. And the bits, I guess this mask is not just that semi-transparent circle, it's actually also fully transparent around the circle. So the bits that lie outside that circle are essentially clipped. They don't come through at all. So that's sort of, you can think of that as more flexible clipping, or you can think of clipping as a specific case of masking. So we've got gradient fills, pattern fills, clipping paths and masks. These are all new things that the R graphics engine understands. So that means that all the graphics packages can say that we'll use this new vocabulary to express themselves and do more things. That's the idea. So why would we need this sort of thing? Well, we've seen one example at the start, which is the Riverplot package. And that's an example of people are already working quite hard to do some things, even though the graphics engine doesn't natively support it. So there are packages that will let you fill regions with patterns, but they have to do a lot of work to get those patterns to draw. So one of the advantages of the new features is that some of those packages won't have to work so hard. They can sort of just ask the graphics engine to do the work for them. A sort of a broader reason for adding some of these things is that I don't like it when I see people drawing stuff and starting a visualization and then having to go somewhere else, like Adobe Illustrator, to finish it off because, and one of the reasons for that is because they can't do what they want in the graphics system. So there's a sort of a general trend towards trying to make it so that you can do whatever you like in the graphics, in the R graphics system. So this is a picture here is an example of something that someone has drawn entirely in R code. So it's not your standard sort of plot, but it includes plot type things like doughnut plots down here and a little dot plot up there and so forth. So I want to try and help people to be able to do this sort of thing entirely in R code, if they can. So that's adding these new features that is heading us in that direction. Another reason for adding some of these things, these new features to the graphics engine is, and this particular interest of mine, is that I like to be able to connect the R graphics system with other graphics systems. And an example of that is the GR import package. And so that takes pictures that have been drawn outside of R and lets you draw them in R, so include them in R. But one of the difficulties with having that integration is that some other systems have all sorts of features that the R graphics system doesn't, and that makes it difficult to reproduce a graphic from somewhere else faithfully inside R. So again, basically by expanding the graphics vocabulary, we're allowing R graphics to talk the same language as other graphics systems. Then generally speaking, it's surprising what people put our graphics to. So there are, I know there are people out there producing generative R with R and so forth. I think that's cool. These new features will hopefully give them more things to play with and produce fun stuff. So I guess I'm not afraid of building a solution for a problem that doesn't yet exist, make the tools more flexible and who knows what people will do. Okay, so that's sort of the simple message. There's new things you can do in R graphics and why that might be useful. I'm going to sort of go into a little tutorial phase now where I'm going to show you how the new graphics features are accessible in R at the moment. So you can go and have a play with them if you want. So the important point, first point is that there's only an R level interface to this stuff in the grid package so far. So if we look at a diagram again, the new features in the graphics engine, I've only made it out as far as the grid package. So there is no duty plot to interface to this yet. There's no interface in the graphics package beyond that yet. I know that some people are working on that sort of stuff. But for now, if you want to have a go at this, then you have to use grid, which I don't think is such a bad thing, but I'll have a little look at some code now so you can try that yourself. So I'll go through each of the new features and we'll see how we can make it work. With linear gradient, there's a linear gradient function. I like the name of that function. And what you do is you define a linear gradient and then when you go to draw something, in this case, I'm drawing a rectangle. You say that the fill for that shape is this linear gradient and then it will fill it in. Another way of doing it is when you push a viewport in grid, which is just a sub region on the page, you can also basically make the default fill for any drawing within that viewport, the gradient as well. So it's a graphical parameter like color or an opaque fill or text size or that sort of stuff. So here's an example of a linear gradient and the idea is I give it a set of colors, at least two. In this case, I'm going to have my gradient go from black to white to black. The gradient is defined by basically a line segment. So I've drawn the start point and the end point of this line segment as dots on this diagram, little pink dots. And then there are stops along the line segment and these stops correspond to where the gradient should hit each of these colors that I've specified in the first argument. So in this case, I'm drawing a rectangle. I'm filling the rectangle with a linear gradient. The linear gradient starts a quarter of the way across and goes to the right-hand side. Within the length of that defined segment at zero, so at the start of the segment, I start with black, halfway across I need to get to white and all the way across to the right-hand side, I need to get back to black and then the system fills in the rest. Radio gradients are very similar. There's a radial gradient function that defines the gradient and then I just say I want the fill of the shape I'm drawing to be that gradient or the default fill to be the gradient if I'm signing it within a viewport. So here's an example, the radial gradients are defined in terms of two circles rather than two endpoints. In this case, I am starting with a circle that is up the top there for a little weeny circle. So the top right, 0.8, 0.8 across the region I'm filling. It's got a very small radius, so it's a tiny little circle and then the end circle for the gradient is centered at the center of the region and its radius is half, so it's basically the size of the region. This gradient starts at white, ends in black and the start is the very first circle and the end is the large circle, the second circle. And again, the system figures out what all the colors in between should be. And not of course restricted to white and black, it's just my imagination that's restricted us to this in this case. Those can be any colors, any color specifications, you know. Okay, pattern fills are slightly more exciting. Again, there's a function that defines the pattern fill and then I just say that I want to fill things with that pattern. Well, set that pattern as the default fill for any shapes I draw in a viewport. Now, the fun thing is that the pattern I want to use is a grid drop. So it's basically I use grids to draw a shape and then that becomes the pattern to fill something else with. Now, this can get sort of a bit hard to think about but when you draw your pattern, you define how big it is basically. Okay, so in this case, in my little diagram here, there's a red square and that's showing how big the pattern is that one defining. The circle that I'm drawing as the shape for the pattern is the red circle and the pattern that I get is the bit of this shape that's inside the size that I've defined. So I'm actually only getting the very top left, top right, bottom right, bottom left arcs on the circle that I've drawn and then I'm saying that this pattern will repeat when it's used to fill a region. So in this diagram, I have used that pattern definition to fill a rectangle. The little arcs on the circle that I drew that have been cut off based on the size of the pattern are then repeated and I end up with these diamonds everywhere. Now, that's not designed just to confuse you, that's to sort of show that we can create sort of interesting patterns and not just dots filling up a page. And the other fun thing is that this shape that I'm drawing that my pattern is going to be based on that can be any grid growth. So that doesn't have to be a single shape. It could be an entire ggplot 2 plot if you wanted it to be. I haven't found a good use for that yet but hopefully somebody will. Right, coming paths. The idea here is that whenever I push a grid viewport, I can say that it has a grid shape as the clip argument. And when grid sees that, it will use that shape that I've defined to be the clipping path. So here's a little example. The path I'm going to use is a circle grog. This circle grog has actually draws two circles. So there's two x values and two y values and they both have the same radius. So that's the two red circles in the diagram. That's the clipping path that I'm defining by basically just drawing something or describing a set of shapes in grid. I then say let's create a viewport with that grog as my clipping path. And that sets the clipping path to be the inside of these two circles. And then on the third line there, I'm drawing a rectangle. That's the red rectangle. That's the boundary of it. And I'm filling that rectangle black. But because I have set this clipping path, I only end up drawing the regions of the rectangle that lie inside the clipping path. So inside either of these two circles. So the black regions are what actually gets drawn. The red lines are there just to sort of give an idea of what's going on. Again, the fun thing is that that clipping path can be any grid shape. So it could be a single shape. It could be a grog that describes multiple shapes like in this example. Or it could be, again, to use a silly example, it could be an entire gg.2 plot that defines a clipping path. Bonus points for the first person who finds a useful that. Right, masks. Very similar to clipping paths. This code here is, that should say mask. So if I'm creating a viewport so I start bridging on the page, I can say that it has a mask argument. Again, that should be mask. And the mask is, again, a grid grog. So using grid to describe shape to be the mask for any subsequent drawing inside that viewport. That's how it should be. So in this case, again, using circles as my mask. So this is similar to the previous one where I was clipping. I have a circle grog that describes two circles. It's two x values, there's two y values, and they're both the same radius. So that's those two red circles. The difference here is that I have filled both of the circles with white. The color really doesn't matter. It's the transparency of those circles that is important in this mask. So one of the circles is a opacity or an alpha value over half. So it's semi-transparent and the other one is fully opaque. So you should hopefully be able to see that this top circle, top right circle is solid red. The bottom left circle appears to be a lighter red. That's because it's actually a semi-transparent circle. Again, the red drawing on this diagram is there just to show what's going on. It's not what would actually be drawn. The mask isn't drawn. The mask just defines a filter that will be used to apply to any other drawing. So that's defined my mask. I then push a viewport and I say this viewport has a mask based on that grob. And then within that viewport, I'm drawing a rectangle. That's the red outline on the red rectangle in the drawing there. And again, I am filling this rectangle black. But because there is a mask in place, the mask is applied to the drawing of the rectangle where the rectangle overlaps the opaque circle. I get exactly the normal rectangle. It's a filled black area, opaque black. The region of the filled rectangle that overlaps with a semi-transparent circle becomes semi-transparent. And so I get a wedge down here that's partially see-through from the rectangle. Again, that mask can be any sort of grob you like. Single shape, and that might be or in this case, several shapes or anything much more complicated. We can sort of combine these things up a bit. So go a little bit more complicated on this one. We have a combination of a mask that is based on a linear gradient. So we'll go through the code. There's a linear gradient being defined at the top there that goes from transparent at the bottom up to opaque white at the top. And that's transparent, not sort of solid color. So the transparency of this linear gradient slowly increases as we go up. That's the important thing about this linear gradient. Then I define a mask as a rectangle that is filled with the linear gradient. So that's basically the shape on the drawing on the left. That's an idea of what my mask looks like. But I'm not going to draw the mask. I'm going to push a viewport and set this mask on that viewport. Up on this line, I'm creating a ggplot2 plot, but not drawing it, just creating an object. And down here, I draw the ggplot2 object that's within the context of this viewport. So this mask is in effect. And so in the middle of the pictures down the bottom there, there is the ggplot2 plot, as it would look normally on the right is what actually happens in this case, which is the ggplot2 plot is drawn with that mask in place. And so the transparency of the ggplot2 plot increases slowly as we go from the bottom to the top. That's combining a linear gradient within a mask. This is going to create a mask using a clipping path. You can see that I'm running out of ideas for what I would use this for. So I'm just showing what it actually just looks like. On the left here, I have a clipping path, which is defined as two circles. So that's the most one of the examples before. I'm drawing a mask or defining a mask as a rectangle that is filled with a semi-transparent white. So it's a see-through rectangle. And the rectangle was drawn inside a viewport with this clipping path. So in the middle picture on the bottom there, that's what the mask looks like. Again, I'm not creating the mask so I can draw the mask. I'm creating the mask so I can use it to apply to other sorts of drawing. So I've created a mask that's basically clipped to a clipping path consisting of two circles. And then finally, once I've pushed a viewport with that mask, I draw a rectangle that is filled white. That's what it would normally look like. But on the right-hand side, that is what actually it gets drawn. So it's the transparency of the mask being applied to the rectangle that I'm trying to draw. And I end up with just the bits of the rectangle that overlap the mask, but they're semi-transparent like the mask is. Okay. Now, so one way of playing with these new features is to write grid code from scratch, like I've been doing in those examples. That's not necessarily what everybody wants to do. So there's a recent package I've just made that's called GGGrid that makes it pretty easy to try out these things in combination with a GGGplot2plot. So the example here is defining a radial gradient. It starts with, you know, which means transparent in the middle and goes to black at the edges. This is a GGplot2plot as normal. And then this is what I can use from this GGgrid package. I can say, well, here's a grog that I want you to draw as part of the GGplot2plot. And so I'm filling the plot region with a rectangle that has a radial gradient fill. And by obscure part of my data, obviously a great thing to do in data visualization. But that just demonstrates the idea of this GGgrid package. Looking at our diagram, that's where GGgrid sits. So it'll let me use GGplot2 and easily add in some wall grid. So I don't have to draw everything with grid. I can start the GGplot2 thing and add a bit of grid to it. Another way I can play with things is through editing the grid objects that other packages create. We don't sort of have time to get into that sort of stuff. If you know how to do that, you can also play with the new features that way. Okay, now those are the new features. That's sort of a bit of how they work and what they might be for, although lots of work by other people needs to be done to demonstrate that they're actually very useful. I just want to look now at a couple of other things that sort of relate to this that are sort of what needs to happen next. So as well as the user interface being limited to grid, so people are... I'll just go back to this diagram. So we've implemented things out to the grid level, but people will be working on hopefully providing a GGplot2 type interface and possibly to other packages to allow you to work with something you're more familiar with and get access to these facilities. Another problem is that the new features are actually only available on certain graphics devices. So there's a list there to get back to our diagram. It looks a bit like this. So we've talked about all how all these packages are stacked up on top of the graphics engine. Below the graphics engine are all the output things like PDF files, PNG files, your screen drawing onto screen devices and so forth. So the sorts of pictures I've been showing with linear gradients and everything in them, they will only work if you use the right device. Let's go back to that list. So they should work no matter where you are if you're producing a PDF file. There's an SPG file with the SPG device and then there's a bunch of variations on some of the devices you might use. So you can get this on screen if you're on Linux, but that's about it. So there are other file-based devices that just don't have it yet. There are people who have written their own graphics devices like the RAG or RRAG package and they're not implemented there to my knowledge. So that's an area where some work could be done. Some of that is up to other people. They can make their graphics device packages do this stuff if they want to. Postcript is one within R that R provides itself that could be updated. The course device on the Mac OS is another important one that would be good to do. Now this is actually one area where people could contribute if they have the required skills. It's not that hard to create your own graphics device. Implementing some of these new features is a bit of work, but if you like to work on Mac OS or if other script is a little thing then it would be a nice thing to actually try and get some of these new features into some more of these packages. The native Windows device on the Windows system, that's not going to get this stuff because the limitations of the system that that device is built on, but if you're using RStudio you probably aren't seeing that anyway. Okay. Another thing that I'm going to be looking at is that there are more things that we could help, we could get the graphics engine to understand. This is a, at least start to get more subtle, but I've already sort of encountered situations where I want to do this stuff so I'm going to put it in. So here's a simple little example of a mask and trying to describe what's going on here. These circles are the things that I want to actually draw. Okay, so that's the middle shape down there. So it's two overlapping opaque white circles. That's what I want to draw. On the left is the mask that I'm going to apply. So that's just a semi-transparent white rectangle. I apply that mask and then I draw the circles. So on the right is the result. And what's happening there is that's showing that when I go, when I've defined this mask, the rectangle on the left, and when I go and draw the circle drop, which is actually two circles, the mask is applied to each circle as it is drawn. And so what I end up with is each individual circle becomes semi-transparent, but there is an overlap in the middle there where I can sort of see the through one circle to the other circle and then through a bit more. So there's sort of this overlap between these two semi-transparent circles. Another way I could do it, which is to rather than apply the mask one at a time to each circle, draw the circles and then apply the mask. And get that was, if I did that, I would get this result on the right here. So the circles are drawn as one sort of result and then the mask is applied as opposed to the mask being applied to each individual circle. So there's some work going on at the moment to try and allow that sort of thing. And there are other examples of extensions where we could teach the graphics engine to speak some more stuff. Okay, calling this future work is a little generous. There are going to be things that don't work right. And one of the useful side effects of people trying to use these new features will be that we'll find out where all the bugs are. So this is just an example where I'm using a mask that is these two rectangles on the left, one's opaque, one's semi-transparent, and I'm trying to apply it to a rectangle that is filled with a pattern and the result is on the right. And the problem here is that the mask is being applied to the pattern as well as to the overall rectangle that I'm drawing. So that's not what I was expecting anyway. So there are going to be things to fix and people trying to stuff out will help us to find those problems. Another big area that the graphics engine doesn't do very well in yet is the drawing text. The right device is sort of solving some of this stuff itself and it would be good to bring some of that into the graphics engine so that we can take advantage of that for everyone. Okay, so the basic idea is that our graphics can now say more things and it would be really cool if people started to use that stuff and did interesting things with it. Some of the images that I've used in this talk have come directly from what other people have done here. And some of the things that I've talked about are described in more detail, especially in the second report here. And there's a link also here to the 2D grid package. That's the basic message right there. Thank you. Well, thank you very much, Paul. That was really informative and exciting. And I just love the way that you just put the stuff out there and just kind of wait and see what people are going to do with it. That's kind of the way that our works and works well. You're not dictating. There's one question in the Q&A, which I'll read, unless Anna, you want to speak. I'm not sure if you can or not, so I'll read it. Thanks a lot for the talk, Paul. I'm not familiar with using grid, but it would be awesome to use some of these features in our Markdown reports for collaborators. Do you have a link to the example code used to make that illustrator-like image or similar ones? So, how are you talking about? I think it's the donut one. Yeah, there. Yeah, I don't have that code, no. But I could, if you contact me, I could find the email for the guy who did it. And he might be able to help you out with that one. I gave a talk on Monday about doing a picture that was nothing like this, but has similar sort of non-standard features in it. And again, I can give you all the code for that one. So, that has examples of some of the more exotic things that you might want to do with our graphics. Sure. Maybe you could put it in the Slack channel or send it to me to put there. Yeah, cool. That would be great. And that is, at least for me, that's such a pain point deciding that trade-off between really working to try to get something in R so that my code is reproducible versus knowing that you can just open up a GUI and slide something over so easily and so quickly. It would be able to do all that kind of stuff right in R without so much pain. Yeah. I mean, one of the difficulties is that if you're going to do it by code, it's still going to be slower than the GUI. And in some cases, more would even, but you have the record of what you've done in terms of reproducibility, in terms of sharing that with other people. It's just incomparable. So, at some point, the benefits outweigh the convenience. That would really be fantastic. So, we have another question in the Q&A from Sherry Zhang. It is, is Gigi Grid supposed to be used on the top level? If so, what's the intuition to introduce grab grid to the top level? So, we're here, right? So, you're all there. I'm not sure what you mean by the top level. So, this Gigi Grid package is meant to be used just like any other standard R package, right? It's nothing fancy or actually particularly clever about it, but it's just a convenient interface. With Gigi Plot 2, you are essentially adding layers of material to your plot. What this is doing is allowing you to add a layer that is just wall grid rather than being something that somebody else has written a Gigi Plot 2 interface for. So, just making a slightly smoother transition between combining Gigi Plot 2 and Grid. Yeah. I mean, if you, I mean, in behind something like Gion Point is some grid code that does the actual drawing. And so, people who are writing Gigi Plot 2 layers are writing grid code for you. But the problem is, if they haven't written something that you need yet, you have to become somebody who can create a Gigi Plot 2 layer. So, this is basically a really simple Gigi Plot 2 layer that lets you just sort of go, right, that's the grid code I want to run that. And so, you can sort of, without having to become a Gigi Plot 2 developer, you can use Grid. Thank you. Another question from Kim Martin. Sorry if I missed it. Can small images made outside of R be included in images made within R? And then the question continues, for example, small icons that would be very hard to make in R, in which wouldn't be expected to change, can be imported and positioned, leaving other parts to a diagram to be programmatically controlled. Right, so this sort of thing with a little data box and boot, perhaps, or this sort of thing where it's actually, there's a logo embedded in the plot. So, yes, the answer is yes. If you've got raster images outside of R, like little icons, then there are packages like the PNG package or the JPG package. They will read those images into R and then you can draw them in R. And there are Gigi Plot 2-based packages that let you do that and Gigi Plot 2-type language as well. So, trying to think of an example, it might actually be Gigi pattern and more Gigi textures. One of those two, possibly that one, might already have an interface that just sort of says, look, give me the name of the file and I'll just draw that image in your picture. I don't know if this is what you had in mind, but I remember there were some fun things going around during the elections here with Gigi Bernie, when there was a meme of Bernie Sanders sitting at an inauguration and you could do any scatterplot and every point would be his faith. I don't know if it was there, but it was a lot of fun. That sort of thing is absolutely possible, yes. And the scenario you're describing is good, right? It's like you've made the picture with something else, but that's not going to change. So, how you made that doesn't matter. That could have been manual or whatever. And then you can, but you can still include that in R with code that means that you can put it wherever you like when you R code and change. So, yes, you can do a Bernie scatterplot of the data and then next week you can update your Bernie plot of the data because it's all R code and it's just pulling in Bernie every time. So, let's move on to the next question from Zao Xu. Xu, thanks for the talk. Very cool features. I have a simple question that for linear gradient, is it more powerful than the roster grub or in which scenario would you suggest people use the linear gradient and in which scenario would you suggest people to use the roster grub? Right, cool. Good question. Good. So, the linear gradient is potentially more efficient because all you are basically saying, just trying to go the right direction, is sort of this, right? It's like, it's almost as small as you could sort of get the information to just say, this is the basics of what needs to happen and then all the details are all done by the system. In some cases, you could express that quite simply as well with the roster grub. You're going to, okay, there's a few things. One is there's a limitation on this guy because it won't work on all graphics devices. Okay. There's a limitation on using a roster grub though because it has an inherent resolution to it. And so, at different scales, it will look horrible. Whereas this, using this linear gradient approach that basically says, here's the description. When you go to draw it, the system will figure out a nice solution, a nice smooth transition, no matter what size it is. So, it's sort of like a vector raster trade-off. And in some cases, depending on how you create your raster grub, you might end up with a large object, much larger than this linear gradient description. I think that covers most of it. So, I guess it's one of those nice, it depends answers. The, sorry, the good thing about, so, like in comparison, the good thing about the raster grub is that it should work pretty much everywhere. And if I just go back to these guys again, that also is a reasonable point for these packages that are doing it the hard way. The good thing about those guys is that they should work on all graphics devices, whereas some of this new stuff will only work on specific graphics devices. So, I'll stop talking. And I don't see, or let me see. Yeah, I think that's all the questions from the Q&A. If I'm allowed to sneak in a quick question on sort of more of the historical perspective on gradient and kind of how you came to it, if it's inspired by other languages or how unique is grid to r? Yeah, okay. So, in terms of there's nothing new under the sun sort of thing, like there's nothing that grubs does that really hasn't previously existed. It's sort of more like an interface. It's the convenience of interface sort of thing. So, the ideas of, for example, viewports in grid, the idea that you create a subregion on the page, and that is now your context for drawing, that's very much like post-script transformation matrices and similar things in OpenGL, where you define a context for drawing, and then you don't have to worry about doing all the conversions of where everything is. You know, you sort of be working a local world, and then you change what your local world is to help you draw things. So, as you alluded to, those ideas come from other languages and other graphic systems. I think, well, I guess the other really strong influence on grid is the original S graphics system, which the graphics package is modeled on, and the sort of the amazing thing about that all the way back then was that it creates, and again, you sort of describe this at the start in your introduction, it created this idea that what you're working with is a flexible graphics system. It's not telling you what you can do, it's giving you a whole bunch of tools and sort of saying, you know, go crazy, and you have fine control over exactly what you want to have happen. So, it's a statistical graphics system built on a generic graphics system sort of idea, and so that heavily influenced the design of grid and trying to carry on that idea and make it even more flexible with the ideas of viewports and coordinate systems, and that sort of stuff. Well, thank you once again. We are out of time, so we are going to continue this discussion on Slack or in other forums, and want to thank Paul once again for sharing this talk with us tonight. Thank you.