给定两个圆:
- 圆C1的圆心坐标为(
x1
,y1
),半径为radius1
- 圆C2的圆心坐标为(
x2
,y2
),半径为radius2
如何计算它们的交集面积?当然可以使用所有标准的数学函数(如sin
、cos
等)。
好的,使用 Wolfram 链接和 Misnomer 的提示查看方程式14,我已经使用我列出的变量和中心之间的距离(可以从它们中轻松地推导出来)推导出了以下 Java 解决方案:
double r = radius1;
double R = radius2;
double d = distance;
if(R < r){
// swap
r = radius2;
R = radius1;
}
double part1 = r*r*Math.acos((d*d + r*r - R*R)/(2*d*r));
double part2 = R*R*Math.acos((d*d + R*R - r*r)/(2*d*R));
double part3 = 0.5*Math.sqrt((-d+r+R)*(d+r-R)*(d-r+R)*(d+r+R));
double intersectionArea = part1 + part2 - part3;
这是一个 JavaScript 函数,完全可以满足 Chris 的需求:
function areaOfIntersection(x0, y0, r0, x1, y1, r1)
{
var rr0 = r0 * r0;
var rr1 = r1 * r1;
var d = Math.sqrt((x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0));
var phi = (Math.acos((rr0 + (d * d) - rr1) / (2 * r0 * d))) * 2;
var theta = (Math.acos((rr1 + (d * d) - rr0) / (2 * r1 * d))) * 2;
var area1 = 0.5 * theta * rr1 - 0.5 * rr1 * Math.sin(theta);
var area2 = 0.5 * phi * rr0 - 0.5 * rr0 * Math.sin(phi);
return area1 + area2;
}
然而,如果一个圆完全在另一个圆内部或两个圆根本没有接触,则此方法将返回NaN。一个稍微不同的版本,在这些情况下不会失败,如下所示:
function areaOfIntersection(x0, y0, r0, x1, y1, r1)
{
var rr0 = r0 * r0;
var rr1 = r1 * r1;
var d = Math.sqrt((x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0));
// Circles do not overlap
if (d > r1 + r0)
{
return 0;
}
// Circle1 is completely inside circle0
else if (d <= Math.abs(r0 - r1) && r0 >= r1)
{
// Return area of circle1
return Math.PI * rr1;
}
// Circle0 is completely inside circle1
else if (d <= Math.abs(r0 - r1) && r0 < r1)
{
// Return area of circle0
return Math.PI * rr0;
}
// Circles partially overlap
else
{
var phi = (Math.acos((rr0 + (d * d) - rr1) / (2 * r0 * d))) * 2;
var theta = (Math.acos((rr1 + (d * d) - rr0) / (2 * r1 * d))) * 2;
var area1 = 0.5 * theta * rr1 - 0.5 * rr1 * Math.sin(theta);
var area2 = 0.5 * phi * rr0 - 0.5 * rr0 * Math.sin(phi);
// Return area of intersection
return area1 + area2;
}
}
我通过阅读Math Forum上找到的信息编写了这个函数。相比于Wolfram MathWorld的解释,我发现这个更清晰。
以下是Python的一个示例。
"""Intersection area of two circles"""
import math
from dataclasses import dataclass
from typing import Tuple
@dataclass
class Circle:
x: float
y: float
r: float
@property
def coord(self):
return self.x, self.y
def find_intersection(c1: Circle, c2: Circle) -> float:
"""Finds intersection area of two circles.
Returns intersection area of two circles otherwise 0
"""
d = math.dist(c1.coord, c2.coord)
rad1sqr = c1.r ** 2
rad2sqr = c2.r ** 2
if d == 0:
# the circle centers are the same
return math.pi * min(c1.r, c2.r) ** 2
angle1 = (rad1sqr + d ** 2 - rad2sqr) / (2 * c1.r * d)
angle2 = (rad2sqr + d ** 2 - rad1sqr) / (2 * c2.r * d)
# check if the circles are overlapping
if (-1 <= angle1 < 1) or (-1 <= angle2 < 1):
theta1 = math.acos(angle1) * 2
theta2 = math.acos(angle2) * 2
area1 = (0.5 * theta2 * rad2sqr) - (0.5 * rad2sqr * math.sin(theta2))
area2 = (0.5 * theta1 * rad1sqr) - (0.5 * rad1sqr * math.sin(theta1))
return area1 + area2
elif angle1 < -1 or angle2 < -1:
# Smaller circle is completely inside the largest circle.
# Intersection area will be area of smaller circle
# return area(c1_r), area(c2_r)
return math.pi * min(c1.r, c2.r) ** 2
return 0
if __name__ == "__main__":
@dataclass
class Test:
data: Tuple[Circle, Circle]
expected: float
tests = [
Test((Circle(2, 4, 2), Circle(3, 9, 3)), 0),
Test((Circle(0, 0, 2), Circle(-1, 1, 2)), 7.0297),
Test((Circle(1, 3, 2), Circle(1, 3, 2.19)), 12.5664),
Test((Circle(0, 0, 2), Circle(-1, 0, 2)), 8.6084),
Test((Circle(4, 3, 2), Circle(2.5, 3.5, 1.4)), 3.7536),
Test((Circle(3, 3, 3), Circle(2, 2, 1)), 3.1416)
]
for test in tests:
result = find_intersection(*test.data)
assert math.isclose(result, test.expected, rel_tol=1e-4), f"{test=}, {result=}"
print("PASSED!!!")
我正在制作一个角色生成工具,基于圆形相交……您可能会发现它有用。
可以动态提供圆:
C: {
C1: {id: 'C1',x:105,y:357,r:100,color:'red'},
C2: {id: 'C2',x:137,y:281,r:50, color:'lime'},
C3: {id: 'C3',x:212,y:270,r:75, color:'#00BCD4'}
},