r - ggplot2:填充两条射线之间的区域

Mar*_*yes 3 r ggplot2

我的情节有来自原点的两条光线.我想以逆时针方向遮蔽从光线1到光线2的区域.我想我可能不得不使用geom_polygon.我希望能够为任意两条任意射线做到这一点,但我似乎无法弄明白.

*我想坚持使用笛卡尔坐标.

这是我的意思的一个例子:

d <- data.frame()

base <- ggplot(d) + xlim(-5, 5) + ylim(-5, 5) + geom_blank()

ray1 <- geom_segment(aes(x=0,y=0,xend=5,yend=4))
ray2 <- geom_segment(aes(x=0,y=0,xend=0,yend=5))

shading <- geom_polygon(data=data.frame(x=c(0,5,5,0), y=c(0,4,5,5)), 
  aes(x,y), fill="blue", alpha=0.2)

base + ray1 + ray2 + shading
Run Code Online (Sandbox Code Playgroud)

对于这个例子,我能够通过检查获得多边形的顶点,但是我将生成几个随机的光线对,我不想每次都手动完成这个过程.

在此输入图像描述

有什么建议?

raw*_*awr 6

这可能有点儿马车和笨重.我想看看更聪明的人提供更优雅的解决方案

但是我不清楚你想如何处理边缘.看例子

shade_segs <- function(ray1, ray2, xlim = c(-5, 5), ylim = xlim) {
  ray <- data.frame(x = c(ray1[1], pmin(ray1[3], xlim[2]),
                          pmin(ray1[3], ylim[2]), ray2[3]),
                    y = c(ray2[1], ray1[4],
                          ## how to handle the edges? :
                          pmin(ray2[4], xlim[2]), pmin(ray2[4], ylim[2])))
                          # pmin(ray1[4], xlim[2]), pmin(ray2[4], ylim[2])))
  # print(ray)
  require(ggplot2)

  ggplot() + xlim(xlim[1], xlim[2]) + ylim(ylim[1], ylim[2]) +
    geom_segment(aes_string(x = ray1[1], y = ray1[2],
                     xend = ray1[3], yend = ray1[4])) +
    geom_segment(aes_string(x = ray2[1], y = ray2[2],
                     xend = ray2[3], yend = ray2[4])) +
    geom_polygon(data = ray, aes(x = x, y = y), fill = "blue", alpha = 0.2) + 
    theme_bw()
}

library(gridExtra)
## c(x0, y0, x1, y1)
l <- list(shade_segs(ray1 = c(0, 0, 5, 4), 
                     ray2 = c(0, 0, 0, 5)),
          shade_segs(ray1 = c(0, 0, 2.5, 2.5), 
                     ray2 = c(0, 0, 1, 5)),
          shade_segs(ray1 = c(0, 0, -1, 5), 
                     ray2 = c(0, 0, -.5, 5)),
          shade_segs(ray1 = c(0, 0, -3, 5), 
                     ray2 = c(0, 0, -.5, -5)))
(do.call(arrangeGrob, c(l, list(nrow = 2, ncol = 2))))
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

或者像这样:

shade_segs <- function(ray1, ray2, xlim = c(-5, 5), ylim = xlim) {
  ray <- data.frame(x = c(ray1[1], pmin(ray1[3], xlim[2]),
                          pmin(ray1[3], ylim[2]), ray2[3]),
                    y = c(ray2[1], ray1[4],
                          ## how to handle the edges? :
                          # pmin(ray2[4], xlim[2]), pmin(ray2[4], ylim[2])))
                          pmin(ray1[4], xlim[2]), pmin(ray2[4], ylim[2])))
  # print(ray)
  require(ggplot2)

  ggplot() + xlim(xlim[1], xlim[2]) + ylim(ylim[1], ylim[2]) +
    geom_segment(aes_string(x = ray1[1], y = ray1[2],
                     xend = ray1[3], yend = ray1[4])) +
    geom_segment(aes_string(x = ray2[1], y = ray2[2],
                     xend = ray2[3], yend = ray2[4])) +
    geom_polygon(data = ray, aes(x = x, y = y), fill = "blue", alpha = 0.2) + 
    theme_bw()
}

library(gridExtra)
## c(x0, y0, x1, y1)
l <- list(shade_segs(ray1 = c(0, 0, 5, 4), 
                     ray2 = c(0, 0, 0, 5)),
          shade_segs(ray1 = c(0, 0, 2.5, 2.5), 
                     ray2 = c(0, 0, 1, 5)),
          shade_segs(ray1 = c(0, 0, -1, 5), 
                     ray2 = c(0, 0, -.5, 5)),
          shade_segs(ray1 = c(0, 0, -3, 5), 
                     ray2 = c(0, 0, -.5, -5)))
(do.call(arrangeGrob, c(l, list(nrow = 2, ncol = 2))))
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

第二个对我来说更有意义.


MrF*_*ick 6

我觉得它会更简单; 但我已经到了这个丑陋的解决方案.这是一个辅助函数,用于计算沿着定义的界限的所有交点base

findslice<-function(seg1, seg2, base=NULL, lim=getlims(base)) {
    getlims<-function(x) {
        list(y=x$scales$get_scales("y")$limits,
        x=x$scales$get_scales("x")$limits)
    }
    gethit<-function(seg, lim) {
        with(seg$mapping, {
            x<-eval(x); y<-eval(y);
            xend<-eval(xend); yend<-eval(yend);
            dx<-(xend-x); dy<-(yend-y)
            bx<-ifelse(dx>0,max(lim$x), min(lim$x))
            by<-ifelse(dy>0,max(lim$y), min(lim$y))
            sx<-ifelse(dx>0,1, 3)
            sy<-ifelse(dy>0,2, 4)
            if(identical(dx,0)) {
                return(list(x=x,y=by, side=sy))
            }
            if (identical(dy,0)) {
                return(list(x=bx,y=y, side=sx))
            }
            nx<-bx
            ny<-(y+dy)*(nx-x)/dx
            side<-sx
            if (abs(ny)>abs(by)) {
                ny<-by
                nx<-(x+dx)*(ny-y)/dy
                side<-sy
            }
            return(list(x=nx, y=ny, side=side))
        })
    }
    p1<-gethit(seg1, lim)
    p2<-gethit(seg2, lim)
    side<-p1$side
    corners<-data.frame(x=lim$x[c(2,1,1,2)], y=lim$y[c(2,2,1,1)])
    r<-data.frame(x=c(seg1$mapping$x, p1$x), y=c(seg1$mapping$y, p1$y))
    while(side != p2$side) {
        r<-rbind(r, corners[side, ])
        side <- (side %% 4) +1
    }
    r<-rbind(r, data.frame(x=p2$x, y=p2$y))
    r
}
Run Code Online (Sandbox Code Playgroud)

这将创建多边形绘图所需的data.frame.例如

base <- ggplot(d) + xlim(-5, 5) + ylim(-5, 5) + geom_blank()

ray1 <- geom_segment(aes(x=0,y=0,xend=5,yend=4))
ray2 <- geom_segment(aes(x=0,y=0,xend=0,yend=5))

shading <- geom_polygon(data=findslice(ray1, ray2, base),
  aes(x,y), fill="blue", alpha=0.2)

base + ray1 + ray2 + shading + ggtitle("Take 1")
Run Code Online (Sandbox Code Playgroud)

这个想法是它将超出限制,然后开始环绕边缘.另一个例子是

ray1 <- geom_segment(aes(x=0,y=0,xend=5,yend=4))
ray2 <- geom_segment(aes(x=0,y=0,xend=0,yend=5))

shading <- geom_polygon(data=findslice(ray1, ray2, base),
  aes(x,y), fill="blue", alpha=0.2)

base + ray1 + ray2 + shading + ggtitle("Take 2")
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述


jlh*_*ard 6

所以这里有两种方法可以做到这一点.第一种使用蛮力方法,这种方法虽然速度很慢但最简单.第二个使用包中的功能集sprgeos操作地图几何.这在概念上类似于使用凸壳的解决方案.

第一种方法:

r1 <- data.frame(x=c(0,5),y=c(0,4))
r2 <- data.frame(x=c(0,0),y=c(0,5))

th1  <- with(r1,atan2(y[2],x[2]))
th2  <- with(r2,atan2(y[2],x[2]))

is.between <- function(x,lo,hi) {
  if (lo<=hi) return(x>=lo & x<=hi)
  return(!(x<lo & x>hi))
}

df    <- expand.grid(x=seq(-5,5,len=200),y=seq(-5,5,len=200))
df$th <- atan2(df$y,df$x)
library(ggplot2)
ggplot(mapping=aes(x,y))+
  xlim(-5,5) + ylim(-5,5)+
  geom_tile(data=df[is.between(df$th,th1,th2),],fill="blue",alpha=.2)+
  geom_line(data=r1)+
  geom_line(data=r2)
Run Code Online (Sandbox Code Playgroud)

这种方法认识到,由于你的光线从原点开始,我们可以创建一个覆盖整个空间的瓷砖网格,然后只是在两条光线扫过的角度之间与x轴形成一个角度.下面的代码以逆时针方式对"之间" r1和"之间"区域r2进行着色.因此,如果r2是第一位的,这将灯罩外部角度(尝试交换的定义r1r2).请注意,我们正在使用geom_tile(...)阴影.

用你的形式主义:

base <- ggplot() + xlim(-5,5) + ylim(-5,5) + geom_blank()
ray1 <- geom_line(data=r1, aes(x,y))
ray2 <- geom_line(data=r2, aes(x,y))
shading <- geom_tile(data=df[is.between(df$th,th1,th2),],aes(x,y),
                     fill="blue",alpha=.2)
base + ray1 + ray2 + shading
Run Code Online (Sandbox Code Playgroud)

第二种方法:

library(sp)      # for SpatialPolygons(...), etc.
library(rgeos)   # for gIntersection(...)
th1  <- with(r1,atan2(y[2],x[2]))
th1  <- ifelse(th1>0, th1, 2*pi+th1)
th2  <- with(r2,atan2(y[2],x[2]))
th2  <- ifelse(th2>0, th2, 2*pi+th2)

th    <- if(th1<th2){seq(th1,th2,length=100)} else {c(seq(th1,2*pi,length=100),seq(0,th2,length=100))}
df    <- data.frame(x=c(0,5*sqrt(2)*cos(th),0),y=c(0,5*sqrt(2)*sin(th),0))
slice <- SpatialPolygons(list(Polygons(list(Polygon(df)),"1")))
box   <- readWKT("POLYGON((-5 -5,-5 5,5 5,5 -5,-5 -5))")
shade <- gIntersection(box,slice)
sh.df <- as.data.frame(shade@polygons[[1]]@Polygons[[1]]@coords)

base <- ggplot() + xlim(-5,5) + ylim(-5,5) + geom_blank()
ray1 <- geom_line(data=r1, aes(x,y))
ray2 <- geom_line(data=r2, aes(x,y))
shading <- geom_polygon(data=sh.df,aes(x,y),fill="blue",alpha=.2)
base + ray1 + ray2 + shading
Run Code Online (Sandbox Code Playgroud)

这种方法利用了特征集rgeos,特别是计算SpatialPolygons交集的能力.它创建了两个SpatialPolygons:一个叫做box简单地覆盖从(-5,-5)到(5,5)的区域,另一个叫做slice两个射线之间的扇形切片.我们必须使用一个半径作为饼,5*sqrt(2)以保证切片至少延伸到角落box.然后我们计算FO使用这两个多边形的交点gIntersection(...)rgeos包装,并使用ggplot绘制它为多边形.

虽然创建多边形的语法是曲折的,但这种方法要快得多.