测试角度是否在范围内(有效且优雅)

Test if angle is in range (efficiently and elegantly)

本文关键字:有效 范围内 是否 测试      更新时间:2023-10-16

我有一个角度phi,我想测试它是否在周期性0..2pi空间中的区间ab内(比如说闭合,但无关紧要)。phiab的值没有限制,特别是:

  • a>b是可能的(例如a=(3/2.)pi,b=pi/;2对应于区间-pi/2…pi/2)
  • 如果是a==b,则间隔为零宽度,并且只有phi==a在内部
  • 有了a-b>=2*piphi将始终在内部

我想出了以下方法:

bool angleInside(const double& phi, double a, const double& b){
   if(std::abs(a-b)>=2*M_PI) return true; // interval covers everything
   if(a>b) a-=2*M_PI;
   if(a==b) return (fmod(a,2*M_PI)==fmod(phi,2*M_PI)); // corner case
   assert(b-a>0 && b-a<2*M_PI); // unless I overlooked something?
   // wrap phi so that a+pphi is in a..a+2*M_PI, i.e. pphi in 0..2*M_PI
   double n=(phi-a)/(2*M_PI); // n in <0..2pi)
   double pphi=(n-floor(n))*(2*M_PI);
   return pphi<(b-a);
}

但我不确定它是否有效,也许没有libs实现这样的东西。

您的条件是矛盾的,特别是条件:

当a-b>=2*pi时,phi将始终在内

有点奇怪。任一:

  1. 您的范围是模空间中的[a,b],即a-b永远不会大于2pi
  2. 您的范围是[a,b]如果(a<b)或[b,a]如果(a>b)和,则应用模

在任何情况下,每当你遇到这样的问题时,都可以方便地从左边界和范围大小来考虑,而不是从左极限和右极限来考虑。

也就是说,如果你能用a==0解决这个问题,那么你就可以用phi' = phi - ab' = b - a来使用这个解决方案。

还要记住,在此类问题中,std::remainder(仅C++11)是您的朋友,因为与fmod不同,它总是会产生(-pi, pi)范围内的结果。

我提出的解决方案是:

bool angleInside(const double phi, const double a, const double b)
{
    //Case 1 above
    const double d = phi - a;
    const double s = std::remainder(b-a - M_PI, 2 * M_PI) + M_PI;
    // Or (Case 2)
    const double d = phi - std::min(a,b);
    const double s = std::fabs(b-a);

    return std::remainder( d - M_PI, 2 * M_PI ) + M_PI <= s;
}

请注意,在这两种情况下,边界情况很难得到一致的结果,尤其是当phi并且落在范围边界模2pi中时。

如果您可以将"double phi"更改为类/结构Angle,它存储角度的cos和sin值,而不是角度值本身。然后你可以很容易地区分,一个角度alpha是在另一个角度beta的"右边"还是"左边"。样品:

struct Angle {
    Angle() : m_cos(1.), m_sin(0.) {} // 0 degree
    // some other methods
    friend double sin( const Angle& a ) { return a.m_sin; }
    friend double cos( const Angle& a ) { return a.m_cos; }
    bool operator<( const Angle& beta ) const {
        return (m_cos * beta.m_sin - m_sin * beta.m_cos) > 0.;
    }
private:
    double m_cos, m_sin;
};

不知道这在您的应用程序中是否有意义。如果是,您可以编程一个间隔

struct Interval {
    Interval( const Angle& from, const Angle& to )
        : m_from( from ), m_to( to ), m_inner( from < to )
    {}
    friend bool in( const Angle& a, const Interval& i ) { // true, if 'a' is in ]from, to[
        if( i.m_inner )
            return i.m_from < a && a < i.m_to;
        return i.m_from < a || a < i.m_to;
    }
private:
    Angle m_from, m_to;
    bool m_inner;
};

使用函数"in"检查角度"a"是否在区间"i"中。

请考虑,没有定义从==到的空(或整体满!)间隔!在这种情况下你需要特别处理。