Saruman
Saruman

Reputation: 153

Slerp interpolation of angle results in -nan(ind)

I'm trying to interpolate a 2D angle and it works 99.9% of the time. For some reason I'm getting -nan(ind) for some values, like:

lastAngle = -0.0613451
currentAngle = -0.061421
alpha = 0.218813

This is the code:

inline float slerpRotation(const float& angle1, const float& angle2, const float& alpha)
{
    auto v1 = b2Vec2{std::cos(angle1), std::sin(angle1)};
    auto v2 = b2Vec2{std::cos(angle2), std::sin(angle2)};
    auto v = this->slerp(v1, v2, alpha);
    return std::atan2(v.y, v.x);
}

inline b2Vec2 slerp(const b2Vec2& v1, const b2Vec2& v2, const float& alpha)
{
    auto cosAngle = v1.x * v2.x + v1.y * v2.y;
    auto angle = std::acos(cosAngle);
    auto angleAlpha = angle * alpha;
    auto v3 = (v2 - (cosAngle * v1)).Normalize();
    auto x = v1.x * std::cos(angleAlpha) + v3 * std::sin(angleAlpha);
    auto y = v1.y * std::cos(angleAlpha) + v3 * std::sin(angleAlpha);
    return b2Vec2{x, y};
}

All this examples results in inf num:

slerpRotation(-0.0613451f, -0.061421f, 0.218813f);
slerpRotation(-1.63139f, -1.63139f, 0.723703f);
slerpRotation(-0.0614404f, -0.0614034f, 0.199831f);
slerpRotation(0.0194162f, 0.0194164f, 0.259074f);

I've tried to solve this problem for a while now without knowing what causes this problem, do you guys happened to know how to solve this?

Upvotes: 1

Views: 500

Answers (1)

Lutz Lehmann
Lutz Lehmann

Reputation: 26040

In the end you are computing

angle1+alpha*(angle2-angle1)

or if you want to exclude some fringe cases,

angle1+alpha*reduce2pi(angle2-angle1)

where

 reduce2pi(phi) = fmod( 3*pi + fmod(phi, 2*pi), 2*pi)-pi

Note that these formulas are completely singularity free, as there is no division. It is not necessary to switch forth and back between angles and their point on the unit circle.

In code, that would be

inline float slerpRotation(const float& angle1, const float& angle2, const float& alpha)
{
    auto angleDiff = angle2-angle1;
    angleDiff = std::fmod(angleDiff, 2*std::M_PI);
    angleDiff = std::fmod(angleDiff + 3*std::M_PI, 2*std::M_PI)-std::M_PI;
    return angle1+alpha*angleDiff;
}

(12/13/2016) combining several comments: If you insist on using exactly this interface structure, then you can get a singularity free method as follows:

inline b2Vec2 slerp(const b2Vec2& v1, const b2Vec2& v2, const float& alpha)
{
    auto angle = std::atan2(v1.x*v2.y - v1.y*v2.x, v1.x*v2.x + v1.y*v2.y);
    auto angleAlpha = angle * alpha;
    auto v3=b2Vec2{-v1.y, v1.x}; // rotation by 90°
    return std::cos(angleAlpha)*v1 + std::sin(angleAlpha)*v3;
}

Upvotes: 2

Related Questions