Проверьте, находится ли угол в диапазоне (эффективно и элегантно)

У меня есть угол phi который я хотел бы проверить, чтобы быть внутри (скажем, закрыт, но не имеет значения) интервал a, b в периодическом 0..2pi пространство. Нет ограничений на значения phi, a а также b, особенно:

  • a>b возможно (например, a = (3/2.) pi, b = pi /; 2 соответствует интервалу -pi / 2 … pi / 2)
  • если a==bинтервал равен нулю и только phi==a будет внутри
  • с a-b>=2*pi, phi всегда будет внутри

Я придумал следующее:

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);
}

но я не уверен, что это эффективно, и, возможно, если нет библиотек, реализующих подобные вещи.

1

Решение

Ваши условия противоречивы, в частности условие:

с a-b> = 2 * пи, фи всегда будет внутри

Это немного странно. Или:

  1. Ваш диапазон [a, b] в пространстве по модулю, то есть a-b никогда не будет больше, чем 2pi,
  2. Ваш диапазон [a, b] if (a < б) или [б, а] если (а> б) и затем Вы применяете по модулю.

В любом случае, когда у вас возникает такая проблема, удобно думать с точки зрения левой границы и размера диапазона, а не левой и правой границ.

То есть, если вы можете решить проблему с a==0, то вы можете просто использовать это решение с phi' = phi - a, а также b' = 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,

0

Другие решения

Если у вас есть возможность изменить ‘double phi’ на класс / структуру Angle, в которой вместо значения угла хранится значение cos и sin угла. Затем Вы можете легко определить, находится ли угол альфа на «правой» или «левой» стороне другого угла бета.
Образец:

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».

Обратите внимание, что пустой (или полностью полный!) Интервал с == до не определен! Вам нужна особая обработка в этом случае.

0