更新:(2023-05-18)合成运算符的行为已在 R 版本 4.3.0 中修改(影响“clear”和“source”运算符)。本文中的示例已更新,以便它们产生相同输出(只是使用不同的运算符)。
在 R 版本 4.1.0 中,为 R 图形引擎添加了对渐变填充、图案填充、剪裁路径和蒙版的支持。
R 的开发版本(可能成为 R 版本 4.2.0)包含对更多图形工具的支持:组、合成运算符和仿射变换,以及对路径和蒙版的一些调整。
已将这些新功能的 R 级接口添加到“grid”图形包中。
library(grid)
以下代码演示如何使用新 grid.group()
函数绘制组。基本思想是我们可以孤立地绘制一组形状,然后将结果添加到主图像中。在本例中,我们在将矩形和圆形添加到图像之前,将它们作为一个独立组绘制。
孤立地绘制组的一个优点是,我们可以使用不同的合成运算符组合形状。在本例中,我们使用“dest.out”运算符,这意味着矩形不会绘制在圆形之上,而是会在圆形中创建一个孔。
首先绘制了一条绿线,以表明圆形中有一个孔,我们可以通过这个孔看到绿线。
grid.segments(gp=gpar(col=3, lwd=50))
grid.group(rectGrob(width=.4, height=.2, gp=gpar(fill="black")),
"dest.out",
circleGrob(r=.4, gp=gpar(col=NA, fill=4)))
以下代码演示了新的路径绘制功能,其中包括用于填充路径的新函数 grid.fill()
。可以从任意数量的形状创建路径,然后我们可以描边或填充路径(或同时进行)。在本例中,我们根据矩形和圆形描述路径。
当路径由重叠的形状组成时,路径的“内部”(即填充的区域)可能会变得复杂。我们可以控制用于决定填充区域的“规则”。在本例中,我们使用“even-odd”规则,这意味着矩形内的区域实际上在路径外部;结果仍然是在圆形中创建一个孔。
路径填充为蓝色(无边框)。
grid.segments(gp=gpar(col=3, lwd=50))
path <- gTree(children=gList(circleGrob(r=.4),
rectGrob(width=.4, height=.2)))
grid.fill(path,
rule="evenodd",
gp=gpar(col=NA, fill=4))
以下代码演示了新的亮度蒙版支持,该支持可通过 as.mask()
函数获得。as.mask()
函数根据一个 grob 和一个 type
创建一个蒙版。
type
可以是 "luminance"
,这意味着 grob 的亮度决定了蒙版输出的半透明度。在本例中,我们根据一个白色圆形(上面绘制了一个黑色矩形)定义一个蒙版。
当我们使用亮度蒙版推送视口时,任何后续绘制都将在蒙版为白色时不透明,蒙版为黑色时透明(蒙版为灰色时半透明)。在这种情况下,在使用蒙版推送视口后,我们用蓝色填充整个图像,结果是一个蓝色圆圈(因为蒙版中的圆圈为白色),并有一个孔(因为蒙版中的矩形为黑色)。
pdf("luminance-mask.pdf", width=2, height=2)
grid.segments(gp=gpar(col=3, lwd=50))
mask <- gTree(children=gList(circleGrob(r=.4,
gp=gpar(col=NA, fill="white")),
rectGrob(width=.4, height=.2,
gp=gpar(col=NA, fill="black"))))
pushViewport(viewport(mask=as.mask(mask, "luminance")))
grid.rect(gp=gpar(fill=4))
dev.off()
其余示例演示仿射变换,使用 grid.define()
函数和 grid.use()
函数。如果我们在一个视口中定义一个组(不绘制它),然后在另一个视口中使用该组,则该组将根据两个视口的相对位置、大小和旋转进行变换。在这种情况下,我们在一个与图像大小相同的视口中,基于一个圆圈和一个矩形(使用“dest.out”运算符,以便矩形在圆圈中创建一个孔)定义一个组,然后推送一个仅为图像高度三分之一的视口,并使用我们定义的组。
这会生成一个垂直压扁的组版本,因为我们使用该组的视口比定义该组的视口短得多。
grob <- groupGrob(rectGrob(width=.4, height=.2, gp=gpar(fill="black")),
"dest.out",
circleGrob(r=.4, gp=gpar(col=NA, fill=4)))
grid.define(grob, name="donut")
grid.segments(gp=gpar(col=3, lwd=50))
pushViewport(viewport(height=1/3))
grid.use("donut")
popViewport()
以下代码类似,但这一次我们在图像宽度三分之一的视口中使用该组,因此该组被水平压扁。
此示例中的另一个区别是,该组基于使用“even-odd”规则填充的路径,这表明我们可以组合组和路径的这些新特性。
grid.newpage()
grob <- fillGrob(path,
rule="evenodd",
gp=gpar(col=NA, fill=4))
grid.define(grob, name="donut")
grid.segments(gp=gpar(col=3, lwd=50))
pushViewport(viewport(width=1/3))
grid.use("donut")
popViewport()
以下代码演示我们可以多次使用一个组。在这种情况下,我们在另外两个视口中使用该组,这两个视口仍然是正方形,但比图像小,并且向左或向右移动。
此示例中的另一个区别是,该组基于在应用了亮度蒙版的视口中绘制的矩形;进一步证明了我们能够将各种图形工具结合在一起使用。
pdf("luminance-mask-squashed.pdf", width=2, height=2)
vp <- viewport(mask=as.mask(mask, "luminance"))
grob <- rectGrob(gp=gpar(fill=4), vp=vp)
grid.define(grob, name="donut")
grid.segments(gp=gpar(col=3, lwd=50))
pushViewport(viewport(x=.25, width=.5, height=.5))
grid.use("donut")
popViewport()
pushViewport(viewport(x=.75, width=.5, height=.5))
grid.use("donut")
popViewport()
dev.off()
到目前为止,新特性仅在图形设备子集中实现:cairo_pdf()
、cairo_ps()
、x11(type="cairo")
、png(type="cairo")
、jpeg(type="cairo")
、tiff(type="cairo")
、svg()
、quartz()
(来自 R 4.3.0)和 pdf()
。此外,大多数合成运算符仅适用于 Cairo 设备或 quartz()
,Cairo 设备仅支持 alpha 蒙版,quartz()
仅支持亮度蒙版。
实现图形设备的 R 包需要针对新 R 版本进行更新和重新安装。
有关新功能的进一步讨论和更多详细信息以及如何实施这些功能,请参阅一系列技术报告:一篇关于组,一篇关于路径,还有一篇关于蒙版。