计算圆和三角形之间的交集面积?

22

如何计算三角形(由三个(X,Y)对指定)和圆形(X,Y,R)之间的交集面积?我已经搜索了一些但没有结果。这是为工作而非学校。:)

在C#中,它看起来会像这样:

struct { PointF vert[3]; } Triangle;
struct { PointF center; float radius; } Circle;

// returns the area of intersection, e.g.:
// if the circle contains the triangle, return area of triangle
// if the triangle contains the circle, return area of circle
// if partial intersection, figure that out
// if no intersection, return 0
double AreaOfIntersection(Triangle t, Circle c)
{
 ...
}
13个回答

0

你需要多精确?如果你可以用简单的形状来近似圆,那么问题就可以简化。例如,将圆建模为一组非常窄的三角形,在中心汇聚。


0
如果三角形的任意一条线段与圆相交,那么纯数学解决方案并不太难。一旦你知道两个交点的位置,就可以使用距离公式来找到弦长。
根据这些方程
ϑ = 2 sin⁻¹(0.5 c / r)
A = 0.5 r² (ϑ - sin(ϑ))

其中c是弦长,r是半径,ϑ是通过圆心的角度,A是面积。请注意,如果超过半个圆被切断,则此解决方案将失效。

如果您只需要一个近似值,那么这可能不值得努力,因为它对实际交点的外观做出了几个假设。


-1

供日后参考,这是我编写的一些Matlab代码来解决这个问题:

https://uk.mathworks.com/matlabcentral/fileexchange/126645-intersection-of-polygon-and-circle

它或多或少地实现了Brian Moths的答案。

以下是代码:

function [A, pointList, isArc, AP] = polygonCircleIntersection(R, P)
% polygonCircleIntersection - calculates area of intersection of circle
% of radius R centered in origin with a 2D polygon with vertices in P, and
% provides shape of intersection
% 
% Inputs:
%   R : radius of circle [scalar]
%   P : 2 X nV matrix of polygon vertices. Each column is a vertex
% Vertices must be in counter-clockwise order or things will go wrong
%
% Outputs:
%   A : area of intersection
%   pointList : list of points on the perimiter of the intersection
%   isArc : array of logicals. isArc(i) is true if the segment between 
%   i-1 and i is an arc.
%   AP : the area of the polygon
%
% Used for FOV analysis
%
% See also: intersect, polyshape, triangulation
%
% Author: Simão da Graça Marto
% e-mail: simao.marto@gmail.com
% Date: 22/03/2023

nV = size(P, 2);
AP = 0;
for i = 1:nV
    AP = AP + shoelace(P(:,i), P(:,mod(i,nV)+1));
end
% SANITY CHECK
% order of points
if(AP<0) 
    error("Polygon must be in counter-clockwise order");
    %If this just means the polygon is not visible, uncomment the
    %following and remove the error:
%     A = 0;
%     pointList = [];
%     isArc = [];
%     return
end
nT2 = sum(P.^2);
isOutside = nT2>R^2;
%1st edge case: all points inside, so polygon fully inside:
if(~any(isOutside))
    A = 0;
    for i = 1:nV
        A = A + shoelace(P(:,i), P(:,mod(i,nV)+1));
    end
    pointList = P;
    isArc = false(1,nV);
    return;
end
% We must start from an outside vertex, so cycle vertices to be in a correct order
shiftK = 1-find(isOutside,1,"first");
P = circshift(P,shiftK, 2);
isOutside = circshift(isOutside,shiftK, 2);
% compute intersection
pointList = []; %list of points forming shape
isArc = [true]; % indicates if i-1 to i is an arc. if not, it's a line segment
outsideCircle = true;
iP = 1;
for i = 1:nV
    x0 = P(:,i);
    d = P(:,mod(i,nV)+1)-x0;
    %segment circle intersection
    [xI, onCircle] = segmentCircleIntersection(x0, d, R);
    pointList = [pointList xI];

    if(~isempty(xI))
        %if point iP is outside circle, then segment from previous point to
        %this one must be an arc
        isArc(iP) = outsideCircle;
        %point iP to iP+1 is always a line segment, by construction
        isArc(iP+1) = false;
    
        outsideCircle = onCircle(2); %if segment goes out of circle, we are now outside circle, otherwise we are inside
        iP = iP + 2; %update point index
    end
end
%edge cases: triangle perimeter fully outside, but is the circle
%inside or outside triangle?
if(isempty(pointList) && all(isOutside))
    polygonContainsOrigin = inpolygon(0,0,P(1,:), P(2,:));
    
    if(polygonContainsOrigin) %2nd edge case: circle fully inside triangle
        A = pi*R^2;
        pointList = [1;0]; %to allow drawing
    else %3rd edge case: triangle fully outside circle
        A = 0;
        pointList = [];
        isArc = [];
    end
    return;
end
%SANITY CHECKS
if(isempty(pointList) && any(isOutside) && ~all(isOutside))
    error("there are no intersections, but some points are inside and others outside")
end
if(any(isArc & circshift(isArc, 1)))
    error("there can't be two arcs in a row")
end
nI = size(pointList, 2);
%compute area as a sum of triangles (shoelace) and arcs
A = 0;
for i = 1:nI
    iPrev = mod(i-2,nI)+1;
    xPrev = pointList(:, iPrev);
    xi = pointList(:, i);
    if(isArc(i))
        thPrev = atan2(xPrev(2),xPrev(1));
        thi = atan2(xi(2),xi(1));
        dth = wrapTo2Pi(thi-thPrev);
        if(dth == 2*pi), dth = 0; end
        A = A + dth*R^2/2;
    else
        A = A + shoelace(xPrev, xi); %can be negative. That is correct.
    end
end
% SANITY CHECK
if(A<0 || A>pi*R^2 || A > AP)
    error("invalid area")
end
end
%Always returns 2 or 0 points. If tangent, none are returned because it
%would have no effect on the area. If segment starts or ends inside circle,
%start or end point are returned. By circle understand it to include its
%interior, so intersection is the segment between the two points returned
function [xI, onCircle] = segmentCircleIntersection(x0, d, R)
    a = sum(d.^2);
    b = 2*x0'*d;
    c = sum(x0.^2) - R^2; %could use precomputed nT2 instead
    D = b^2 - 4*a*c;
    if(D<=0) % line never goes in the circle
        xI = [];
        onCircle = [];
        return
    end
    %intersection points (along segment coordinate)
    ll = (-b + [-1 1]*sqrt(D) )/(2*a);
    % SANITY CHECK
    if( ll(1) >= ll(2) )
        error("A mathematical impossibility. Did you give this thing complex numbers or something?")
    end
    if( ll(2)<0 || ll(1)>1) %intersection fully outside segment
        xI = [];
        onCircle = [];
        return
    end
    %compute intersection
    ll = max(0, ll);
    ll = min(1, ll);
    
    xI = x0 + ll.*d;
    onCircle = [ll(1)>0 ll(2)<1];
end
%Shoelace formula for area of a straight segment with edges connected to
%origin, forming a triangle.
%can be negative if area is meant to be subtracted
function A = shoelace(p1, p2)
    A = (p1(1)*p2(2) - p1(2)*p2(1))/2;
end

虽然这理论上回答了问题,但最好在此处包含答案的基本部分并提供参考链接。请编辑答案以包含所有相关信息。确保使用自己的话,完全引用的答案(无论是否有来源)通常会被删除,因为它们不包含任何原创内容。 - Adriaan
这是一段代码,它可以获得原始问题所寻找的答案。还有什么比这更相关的呢?Brian Moth的回答已经包含了答案的每个关键部分,这只是为了节省人们自己实现它所需的时间而提供的代码。如果该网站的用户仍然认为这不是一个有用的答案,我将简单地将其删除。 - Sirplentifus
1
这是非常相关的。然而,Stack Overflow 要求回答能够在不依赖链接的情况下使用。这是由于多种原因:人们可能无法(例如受到系统管理员的限制)访问某些链接,链接可能在未来某个时候失效等等。因此,你需要(并且被鼓励)发布一个可以在不点击任何链接的情况下使用的答案,并提供链接作为参考。 - Adriaan
1
我明白了。我在这里添加了代码,希望这会成为一个有用的答案。感谢您的建议。 - Sirplentifus

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接