代码之家  ›  专栏  ›  技术社区  ›  Gaius Augustus

如何计算给定方向上的最小平移向量?

  •  4
  • Gaius Augustus  · 技术社区  · 11 年前

    分离轴定理返回任意方向上的最小平移向量。这个问题的一个变体是计算给定方向上的最小平移向量。我想知道如何用多边形和圆来计算这一点,但我陷入了圆-圆的情况。有人知道如何解决这个问题吗?

    此函数的签名为:

    Vector minimum_tranlation_vector_between(Circle a, Circle b, Vector axis);
    

    提前感谢!

    2 回复  |  直到 11 年前
        1
  •  3
  •   samgak    11 年前

    假设圆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()之前检查值的符号。

        2
  •  3
  •   Lutz Lehmann    11 年前

    复制samgak中的命名方案,但将所有内容简化为二次方程的标准情况给出了程序

    double calculateSeparationDistance(double circle1x, double circle1y, double radius1, double circle2x, double circle2y, double radius2, double vectorx, double vectory)
    {
        double dx = circle1x-circle2x;
        double vx = vectorx;
        double dy = circle1y-circle2y;
        double vy = vectory;
        double R2 = (radius1 + radius2) * (radius1 + radius2);
    
        // the equation is 
        // (dx+vx*d)²+(dy+vy*d)² = R2
        // expanding and reordering by degree
        // (vx²+vy²)*d²+2*(vx*dx+vy*dy)*d+(dx²+dy²-R2) = 0
    
        double a = vx*vx;
        double b = 2*(vx*dx+vy*dy);
        double c = dx*dx+dy*dy-R2
    
        // Note that we want the smaller (in absolute value) solution, 
        // and that the selection by the solution formula depends on
        // the sign of b. Setting (b,d):=(-b,-d) still results in an
        // equation with a solution.
    
        double sign = 1;
        if ( b<0 ) { b=-b; sign = -1; } 
    
        // Real solutions do not exist if the discriminant is negative
        // here that means that abs(dx*vx+dx*vy) is too small, 
        // geometrically that the circles never touch if moving in
        // direction v.
    
        if( b*b < 4*a*c ) return 0; // or 1e308 or throw exception
    
        // apply stable standard solution formula for the smaller solution:
        double d = -(2*c)/(b + Math.sqrt(b*b-4*a*c));
    
        return sign*d;
    }