2014-07-24 44 views
0

我的应用程序有几个圆以任意角度旋转,我需要测试它们的旋转是否相等(相对于一些误差)。一个天真的执行是这样的:测试角度相等的算法

function angleEquals(first, second, errorMargin) { 
    return Math.abs(first - second) <= errorMargin; 
} 

但是这样做失败了,因为旋转可能会增长到无穷大。因此,我们需要规范化两个角度:

function normalizeAngle(angle) { 
    angle = angle % (2 * Math.PI); // normalize the angle to the interval [-2 * pi, 2 * pi] 
    if (angle < 0) 
     return 2 * Math.PI + angle; // normalize the angle to the interval [0, 2 * pi] 
    return angle; 
} 

这工作几乎总是但也有一些角落情况下失败。例如 angleEquals(0, 2 * Math.PI - 1e-4, 1e-3)false

有什么建议吗?

回答

5

您需要规范角度之间的差异,而不是角度本身。

diff = angle1-angle2 // calculate the difference 
diff = diff % 2*PI // get it into the range [0, 2*PI) 
if(diff > PI) diff -= 2*PI // get it into the range (-PI, PI] 
diff = abs(diff) // get the absolute difference in the range [0,PI] 
return (diff < errorMargin) 

有些事情你可以采取措施收紧一点,但它会显示你的基本步骤顺序。编辑:为了后人的缘故,这里是更严格的版本。

diff = abs((abs(angle1-angle2) + PI) % (2*PI) - PI) 
return (diff < errorMargin) 

(这一个不依赖于模量在负数的行为,而且是少枝)

+0

如果* angle1 *可以小于* angle2 *,那么'diff = diff%2 * PI'不会导致'[-2PI,+ 2PI]'的范围。另外,如果'angle1 =0π'和'angle2 =1.9π',它们之间的角度是'0.1π',而上述将返回'0.9π'。 – RobG

+0

请注意,当您可能打算使用'diff%(2 * PI)'时,'diff%2 * PI'等于'(diff%2)* PI'。 – RobG

+0

@RobG我使用模块化算术的“正常”定义,其结果总是正面的。不幸的是,这与C的标准模量函数不同。 (我上面的语法快速而松散......当然'''''对于浮点数不会做任何事情。)你可以通过加2 * PI和做第二个模数来解决这个问题。 – Sneftel

1
function angleEquals(first, second, errorMargin) { 
    var diff = Math.abs(first - second) % (2 * Math.PI); 
    return Math.min(diff, Math.abs(diff - 2 * Math.PI)) <= errorMargin; 
} 
0

当旋转测试,我认为你总是想要测量的最短两者之间的角度,例如如果一个旋转0°而另一个旋转359°,那么它们之间的角度是1°,而不是359°。

下面的函数获得两者之间的最小角度,并将其与容差:

function anglesInTolerance(angle1, angle2, tolerance) { 

    // Radians in half a circle (180 deg) = 3.141592653589793 
    var pi = Math.PI; 

    // Get the difference in the angles 
    var diff = Math.abs(angle1 - angle2) 

    // Reduce to range +/- 2pi 
    diff = diff % (2*pi); 

    // If the difference is greater than pi (180 deg), subtract from 2pi 
    if (diff > pi) diff = 2*pi - diff; 

    return diff <= tolerance 
} 

var pi = Math.PI; 
console.log(anglesInTolerance(0.0*pi, 1.9*pi, 0.1*pi)); // true 
console.log(anglesInTolerance(0.5*pi, 0.4*pi, 0.1*pi)); // true 
console.log(anglesInTolerance(0.4*pi, 0.5*pi, 0.1*pi)); // true 
console.log(anglesInTolerance(0.1*pi, 1.9*pi, 0.1*pi)); // false 

请注意,以避免舍入误差,可能要四舍五入的数字说3或4位小数其中对你的宽容也许就足够了,例如如果你想比较±0.0001,那么四舍五入到小数点后4位。

舍入到小数点后6位,该函数可以是:

// Values are in radians 
function anglesInTolerance(angle1, angle2, tolerance) { 
    var pi = Math.PI; 
    var diff = Math.abs(angle1 - angle2) % (pi*2); 

    if (diff > pi) diff = pi*2 - diff; 
    return diff.toFixed(6) <= tolerance.toFixed(6); 
} 

console.log(anglesInTolerance(0.1*pi, 6.2*pi, 0.1*pi)); // true 
console.log(anglesInTolerance(0.05*pi, 5.95*pi, 0.1*pi)); // true 

假设6位小数是足够的,如果需要的话,可以使用更高的精度。