Matlab:为什么填充函数不填充两个圆圈之间的区域?

Matlab: Why is the fill function not filling the area between two circles?

我试图在 Matlab 中填充两个圆之间的相交区域。我完全从 Matlab Central 上的 this 文章中复制并粘贴了这段代码。

t = linspace(0, 2*pi, 100);
cir = @(r,ctr) [r*cos(t)+ctr(1); r*sin(t)+ctr(2)]; % Circle Function
c1 = cir(1.0, [0; 0]);
c2 = cir(1.5, [1; 1]);
in1 = find(inpolygon(c1(1,:), c1(2,:), c2(1,:), c2(2,:))); % Circle #1 Points Inside Circle #2
in2 = find(inpolygon(c2(1,:), c2(2,:), c1(1,:), c1(2,:))); % Circle #2 Points Inside Circle #1
[fillx,ix] = sort([c1(1,in1) c2(1,in2)]); % Sort Points
filly = [c1(2,in1) (c2(2,in2))];
filly = filly(ix);
figure(1)
plot(c1(1,:), c1(2,:))
hold on
plot(c2(1,:), c2(2,:))
fill([fillx fliplr(fillx)], [filly fliplr(filly)], 'g', 'EdgeColor','none')
hold off
axis square

我最终得到的是下图:

但是,它应该显示为这个图像:

为什么没有像示例文章中那样填充区域?

如果您有 Mapping Toolbox,您可以使用 polybool to find the intersection between to polygones, and than patch (which dosen't require Mapping Toolbox, and is better than fill) to draw it. The folowing code works even without the first 2 lines that use poly2cw,但它会发出一些警告。这可以通过 poly2cw 转换来解决:

[c1(1,:), c1(2,:)] = poly2cw(c1(1,:), c1(2,:)); % clock-wise transform
[c2(1,:), c2(2,:)] = poly2cw(c2(1,:), c2(2,:)); % clock-wise transform   
[xb, yb] = polybool('intersection',c1(1,:),c1(2,:),c2(1,:), c2(2,:));
plot(c1(1,:), c1(2,:))
hold on
plot(c2(1,:), c2(2,:))
patch(xb, yb, 1, 'FaceColor', 'g','EdgeColor','none')
axis equal

问题中的代码无法运行,因为它在 fillxfilly 中的点的顺序有一些错误。我们可以看到,如果我们将 'EdgeColor' 设置为可见,并沿着补丁的外围线(见下图,这里减少到 20 个点,用于说明)。我们可以清楚地看到,要填充的多边形的点排列在圆之间的'zig-zag'中,所以它根本没有面积。我已经对从每个圆中取出的多边形顶点进行了编号,以演示 fill 函数读取它们的顺序。

为了用颜色填充圆之间的所有交点,我们需要通过圆上相交的点(in1in2)来定义 'polygon'正确的顺序。这意味着如果假想的铅笔按照给定的顺序在它们之间画一条线,我们希望它们形成一个闭合的形状。像这样:

我们从其中一个圆圈中的 1 开始,一直持续到该圆圈上的数字结束,然后移动到另一个圆圈中的 1,当我们到达第二个圆圈的尽头时,我们关闭多边形将最后一点连接到第一个点。正如您在上图中看到的那样,圆圈的起点和终点非常接近,所以我们得到两个数字在彼此之上。

如何正确排序点数?

我们首先按照问题中的描述获取 in1in2。我们来看看in1:

in1 =
     1     2     3     4     5     6     7    19    20

这些是c1要取的点的索引,它们看起来是有序的,但包含一个间隙。这个差距是因为inpolygon按照c1中的顺序检查点,而c1的起点在交集区域内。所以我们得到前 7 个点,然后我们离开交叉路口,当我们到达点 19 和 20 时我们返回。但是,对于我们的多边形,我们需要这些点从最近的点开始到其中一个地方圆相交的地方,然后绕着圆走,直到我们到达第二个交点。

为此,我们在积分顺序中查找 'gap':

gap = find(diff(in1)>1);

并正确重新排序:

X1 = [c1(1,in1(gap+1:end)) c1(1,in1(1:gap))];
Y1 = [c1(2,in1(gap+1:end)) c1(2,in1(1:gap))];

但是,可能没有'gap',正如我们在in2中看到的:

in2 =
    11    12    13    14

所以我们需要将其包装在 if 中以检查是否需要重新排序这些点:

if ~isempty(gap)
    X1 = [c1(1,in1(gap+1:end)) c1(1,in1(1:gap))];
    Y1 = [c1(2,in1(gap+1:end)) c1(2,in1(1:gap))];
else
    X1 = c1(1,in1);
    Y1 = c1(2,in1);
end

现在我们需要做的就是连接 X1X2(对于圆圈 2),对于 Y 也是如此,并使用 patch (类似于 fill,但更好)绘制它:

patch([X1 X2],[Y1 Y2],'g','EdgeColor','none')

有 20 个点的圆不是真正的圆,交点有部分颜色,所以这里是完整的代码和 200 个点的结果:

t = linspace(0, 2*pi, 200);
cir = @(r,ctr) [r*cos(t)+ctr(1); r*sin(t)+ctr(2)]; % Circle Function
c1 = cir(1.0, [0; 0]);
c2 = cir(1.5, [1; 1]);
plot(c1(1,:), c1(2,:))
hold on
plot(c2(1,:), c2(2,:))
axis equal
in1 = find(inpolygon(c1(1,:), c1(2,:), c2(1,:), c2(2,:)));
in2 = find(inpolygon(c2(1,:), c2(2,:), c1(1,:), c1(2,:)));
gap = find(diff(in1)>1);
if ~isempty(gap)
    X1 = [c1(1,in1(gap+1:end)) c1(1,in1(1:gap))];
    Y1 = [c1(2,in1(gap+1:end)) c1(2,in1(1:gap))];
else
    X1 = c1(1,in1);
    Y1 = c1(2,in1);
end
gap = find(diff(in2)>1);
if ~isempty(gap)
    X2 = [c2(1,in2(gap+1:end)) c2(1,in2(1:gap))];
    Y2 = [c2(2,in2(gap+1:end)) c2(2,in2(1:gap))];
else
    X2 = c2(1,in2);
    Y2 = c2(2,in2);
end
patch([X1 X2],[Y1 Y2],'g','EdgeColor','none')
hold off 


上面提到的所有内容都可以替换为在相交的顶点上使用 convhull 并给出相同的结果:

x = [c1(1,in1) c2(1,in2)]; % all x's for intersecting vertices
y = [c1(2,in1) c2(2,in2)]; % all y's for intersecting vertices
k = convhull(x,y); % calculate the convex polygon
patch(x(k),y(k),'g','EdgeColor','none')