2014-07-24 83 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位小數是足夠的,如果需要的話,可以使用更高的精度。