如何在R的grid包中组合grob以创建闭合可填充图形?
问题描述
我正在编写一个ggplot2扩展,过程中尝试用grid包创建填充形状,其中部分边由functionGrob定义。我不清楚如何将grob树转换为闭合形状以进行填充。我查阅了《R Graphics v3》但没有找到解决方法,想了解这在grid中是否可行?或是在gridSVG或gridGeometry中可行?
以下是最简示例:
library(grid) grid.newpage() pushViewport(viewport(xscale=c(0, 2*pi), yscale=c(0,1.1), default.units = "native")) bottom <- linesGrob(x=c(pi/4, (3/4)*pi), y=c(0.1, 0.1), default.units = "native") top <- functionGrob(function(x) list(x=x, y=sin(x)), range=c(pi/4, 3*pi/4)) right <- linesGrob(x=c(3*pi/4, 3*pi/4), y=c(0.1, sin(pi/4)), default.units = "native") left <- linesGrob(x=c(pi/4, pi/4), y=c(sin(pi/4), 0.1), default.units = "native") all <- grobTree(bottom,right,top,left) grid.fill(all, gp=gpar(fill=rgb(1,0,0))) # 仅填充了正弦曲线的路径 grid.stroke(all, gp=gpar(lwd = 2, col=rgb(0,1,0))) # 所有路径被描边
目前grid.fill是对每个grob单独填充,而非组合形成的闭合形状。我要绘制的图形更复杂,无法简单通过组合functionGrob和rectGrob来填充。
解决方案
方法一:手动合并坐标生成闭合路径Grob
grid本身无法直接将grob树中的多个独立路径合并为单一闭合区域,但可以通过提取所有路径的坐标,按闭合顺序拼接后生成pathGrob来实现填充:
library(grid) grid.newpage() pushViewport(viewport(xscale=c(0, 2*pi), yscale=c(0,1.1), default.units = "native")) # 生成functionGrob对应的坐标序列(用足够多的点保证曲线平滑) x_range <- c(pi/4, 3*pi/4) x_seq <- seq(x_range[1], x_range[2], length.out = 200) top_y <- sin(x_seq) # 按闭合顺序拼接所有边的坐标:左竖边起点 → 顶部曲线 → 右竖边 → 底部横边 → 回到起点 path_x <- c( x_range[1], # 左竖边顶端 x_seq, # 顶部曲线x坐标 x_range[2], # 右竖边顶端 x_range[2], # 右竖边底端 x_range[1], # 左竖边底端 x_range[1] # 回到起点闭合 ) path_y <- c( sin(x_range[1]), # 左竖边顶端y值 top_y, # 顶部曲线y坐标 sin(x_range[2]), # 右竖边顶端y值 0.1, # 右竖边底端y值 0.1, # 左竖边底端y值 sin(x_range[1]) # 回到起点闭合 ) # 创建闭合路径Grob并绘制 filled_shape <- pathGrob( x = path_x, y = path_y, default.units = "native", gp = gpar(fill = rgb(1,0,0), col = rgb(0,1,0), lwd = 2) ) grid.draw(filled_shape)
这种方法的核心是将所有边的坐标按顺时针/逆时针的闭合顺序拼接,确保路径首尾相连,这样pathGrob就能识别为单一闭合区域进行填充。
方法二:使用gridGeometry包合并图形
如果图形过于复杂,手动提取坐标难度大,可以借助gridGeometry包的多边形处理功能,将多个grob转换为多边形后合并:
library(grid) library(gridGeometry) grid.newpage() pushViewport(viewport(xscale=c(0, 2*pi), yscale=c(0,1.1), default.units = "native")) # 定义原grob树 bottom <- linesGrob(x=c(pi/4, (3/4)*pi), y=c(0.1, 0.1), default.units = "native") top <- functionGrob(function(x) list(x=x, y=sin(x)), range=c(pi/4, 3*pi/4)) right <- linesGrob(x=c(3*pi/4, 3*pi/4), y=c(0.1, sin(pi/4)), default.units = "native") left <- linesGrob(x=c(pi/4, pi/4), y=c(sin(pi/4), 0.1), default.units = "native") all <- grobTree(bottom, right, top, left) # 将grob转换为多边形(需先渲染grob获取坐标) grid.draw(all) polys <- grobToPolygons(all) # 合并多边形为单一闭合区域(需确保多边形能组成闭合形状) merged_poly <- polyclipGrob(polys, op = "union") # 绘制合并后的填充形状 grid.fill(merged_poly, gp = gpar(fill = rgb(1,0,0))) grid.stroke(merged_poly, gp = gpar(lwd = 2, col = rgb(0,1,0)))
注意:grobToPolygons需要先绘制grob才能提取坐标,且合并效果依赖于原grob的路径是否能形成连续的闭合边界,复杂场景下可能需要调整参数。
关于gridSVG
gridSVG主要用于将grid图形导出为SVG格式,本身并不直接提供合并路径填充的功能,因此更推荐上述两种方法。
内容的提问来源于stack exchange,提问作者ksdjnf
相关产品推荐
相关产品推荐

