最近,在看cone tracing中涉及求圆锥和立方体的相交的需要。发现这十分复杂,所以想退而求其次求圆锥和球体的相交,再做简化,求二维圆和圆的相交面积。
然而求二维圆和圆的相交面积,也有一些复杂。如果先求2个圆的交点,可以辅助相交面积的求解,所以先写了个程序验证2圆交点求解。
首先,根据圆的一般方程,我们如果有2个圆的中心坐标(c1x,c1y)、(c2x,c2y),以及2个圆的半径r1和r2,我们可以列出下面2个等式:
1)
2)
把2式减去1式,可以得到:
3)
这个公式项目较多,做一些变量替换,可以简化这个公式,令:
4)
5)
6)
把式4、5、6代入3式,可以得到:
7)
然后把7代入1:
8)
整理一下,得式9:
9)
最后得到的这个关于x的1元2次方程就可以直接求解了,先提取一下系数:
10)
11)
12)
根据求根公式,得到解x1和x2,如果根式里小于0,则没有解,即2圆不相交。解出x1、x2后代入7式,可以解得y1和y2,2个相交坐标点即可求出。
13)
到这里,一般的求解推理就完成了,但是回顾式7,可以发现,这里可能会有个除零异常发生。即两圆的中心Y坐标相同(c1y=c2y)时,C为0,这时就不能按上述流程求解了。
不过(c1y=c2y)时,求解简化了,此时x只有1个解,从公式3,我们得出x的解为:
最后函数可以如下实现:
bool CircleIntersect(Vec2 c1, float r1, Vec2 c2, float r2, Vec2 *intersectP1, Vec2 *intersectP2)
{
float centerD = c1.Distance(c2);
if (centerD < r1 + r2){
float A = r2 * r2 - r1 * r1 - c2.x * c2.x + c1.x * c1.x - c2.y * c2.y + c1.y * c1.y;
float B = 2 * (c1.x - c2.x);
float C = 2 * (c1.y - c2.y);
if (C == 0){
if (B == 0){ // center are the same
return false;
}
else{
float x = A / B;
float a = 1;
float b = -2 * c2.y;
float c = x * x - 2 * c2.x * x + c2.x * c2.x + c2.y * c2.y - r2 * r2;
float delta = b * b - 4 * a * c;
if (delta >= 0.0f){
float sqrtD = powf(delta, 0.5f);
float y1 = (sqrtD - b) / (2 * a);
float y2 = (-sqrtD - b) / (2 * a);
*intersectP1 = Vec2(x, y1);
*intersectP2 = Vec2(x, y2);
return true;
}
}
}
else{
float a = B * B / (C * C) + 1;
float b = 2 * c1.y * B / C - 2 * c1.x - 2 * A * B / (C * C);
float c = c1.x * c1.x + c1.y * c1.y + A * A / (C * C) - 2 * c1.y * A / C - r1 * r1;
float delta = b * b - 4 * a * c;
if (delta >= 0.0f){
float sqrtD = powf(delta, 0.5f);
float x1 = (sqrtD - b) / (2 * a);
float x2 = (-sqrtD - b) / (2 * a);
float y1 = (A - B * x1) / C;
float y2 = (A - B * x2) / C;
*intersectP1 = Vec2(x1, y1);
*intersectP2 = Vec2(x2, y2);
return true;
}
}
}
return false;
}
函数参数为2圆的中心和半径;如果2圆相交,返回true且把交点通过指针回传,没有相交则返回false。
注:如果2圆中心重合(B与C为0)则视为不相交(即使半径相等时会有无数个交点);另外如果两圆心距离小于半径之和才进入求交点计算,可以省下很多计算量。
讨论:计划计算2圆的相交面积,现在想到的方法是计算相交后得到的2个扇形面积减去扇形重合的区域。这个方法应该能精确计算出相交面积。但是要实施到cone tracing中,有1个问题:需要把3维2球体投射到2维平面上求解。这点不是很好克服,所以可能会转而使用一个近似的方法:计算2圆中心距离和2圆半径的关系。比如:当2圆分离时,2圆中心距离>2圆半径之和;当圆1包含圆2(圆1半径>圆2半径)时,圆1半径减2圆中心距离>圆2半径(见下图)。