假设圆a的中心坐标和半径分别由a.x、a.y和a.r.给出。
还假设我们有一个向量 v,我们想通过将第一个圆沿 v 的方向移动 d 量来将圆推开。圆圈将在以下情况下相互接触:
((a.x + (v.x * d)) - b.x)2 + ((a.y + (v.y * d)) - b.y)2 = (a.r + b.r)2
这个等式只是计算偏移圆心之间的平方距离,并将其设置为半径之和的平方。
我们可以通过移动两个圆来简化它,方法是从两个圆中减去第二个圆的中心点,使第二个圆位于原点,从而将其从等式中删除:
(a.x + (v.x * d))2 + (a.y + (v.y * d))2 = (a.r + b.r)2支持>
现在我们只需要求解 d 并将其乘以 v 即可得到结果(分离向量)。我作弊并使用了online solver,有两种解决方案(格式化为代码,因为格式化程序一直试图将乘法转换为斜体):
d = -(sqrt((v.y²+v.x²)*e-a.x²*v.y²+2*a.x*v.x*a.y*v.y-v.x²*a.y²)+a.y*v.y+a.x*v.x)/(v.y²+v.x²)
d = (sqrt((v.y²+v.x²)*e-a.x²*v.y²+2*a.x*v.x*a.y*v.y-v.x²*a.y²)-a.y*v.y-a.x*v.x)/(v.y²+v.x²)
以下是 Java 中的一些概念验证代码:
double calculateSeparationDistance(double circle1x, double circle1y, double radius1, double circle2x, double circle2y, double radius2, double vectorx, double vectory)
{
double a = circle1x-circle2x;
double b = vectorx;
double c = circle1y-circle2y;
double d = vectory;
double e = (radius1 + radius2) * (radius1 + radius2);
// 2 possible solutions:
double distance = -(Math.sqrt(((d*d)+(b*b))*e-(a*a)*(d*d)+2*a*b*c*d-(b*b)*(c*c))+c*d+a*b)/((d*d)+(b*b));
// double distance = (Math.sqrt(((d*d)+(b*b))*e-(a*a)*(d*d)+2*a*b*c*d-(b*b)*(c*c))-c*d-a*b)/((d*d)+(b*b));
// add (distance * vectorx, distance * vectory) to first circle, or subtract from second circle to separate them
return distance;
}
这两种解决方案对应于将第一个圆圈从第二个圆圈中向任一方向推出。您可以选择绝对值最小的任一解,或选择带正号的解。
失败案例:当圆不相交并且向量没有将它们置于碰撞路线上时,则没有解,并且方程给出了负值的平方根,因此要么事先进行相交测试,要么检查将值传递给 sqrt() 之前的符号。