Определите, находится ли угол между двумя другими углами
Я пытаюсь выяснить, находится ли угол между двумя другими углами. Я пытаюсь создать простую функцию для этого, но ни один из моих методов не будет работать для всех возможных значений углов.
Можете ли вы помочь мне изменить мою функцию, чтобы правильно определить, находится ли угол между двумя другими углами?
![enter image description here]()
На приведенном выше рисунке; Я использую зеленую точку в качестве центральной точки, затем определяю угол каждой линии на зеленую точку. Затем я вычисляю угол черной точки на зеленую точку. Я пытаюсь проверить, является ли угол черной точки МЕЖДУ двумя углами линии.
ПРИМЕЧАНИЕ. В моем случае; угол (targetAngle), как говорят, лежит между двумя другими углами, ЕСЛИ разница между двумя углами равна < 180 градусов и TargetAngle лежит в полости, сделанной этими двумя углами.
Следующий код должен работать, но он не подходит для них (которые лежат между углом):
- is_angle_between (150, 190, 110)
- is_angle_between (3, 41, 345)
bool is_angle_between(int target, int angle1, int angle2)
{
int rAngle1 = ((iTarget - iAngle1) % 360 + 360) % 360;
int rAngle2 = ((iAngle2 - iAngle1) % 360 + 360) % 360;
return (0 <= rAngle1 && rAngle1 <= rAngle2);
}
// Example usage
is_angle_between(3, 41, 345);
Другой метод, который я попытался, который также не работает:
int is_angle_between(int target, int angle1, int angle2)
{
int dif1 = angle1-angle2;
int dif2 = angle2-angle1;
int uDif1 = convert_to_positive_angle( dif1 ); // for eg; convert -15 to 345
int uDif2 = convert_to_positive_angle( dif2 );
if (uDif1 <= uDif2) {
if (dif1 < 0) {
return (target <= angle1 && target >= angle2);
}
else return (in_between_numbers(iTarget, iAngle1, iAngle2));
}
else {
if (dif2 < 0) {
return (target <= angle1 && target >= angle2);
}
else return (in_between_numbers(iTarget, iAngle1, iAngle2));
}
return -1;
}
Ответы
Ответ 1
bool is_angle_between(int target, int angle1, int angle2)
{
// make the angle from angle1 to angle2 to be <= 180 degrees
int rAngle = ((angle2 - angle1) % 360 + 360) % 360;
if (rAngle >= 180)
std::swap(angle1, angle2);
// check if it passes through zero
if (angle1 <= angle2)
return target >= angle1 && target <= angle2;
else
return target >= angle1 || target <= angle2;
}
Ответ 2
Вдохновленный сообщением о Интервалы в модульной арифметике:
static bool is_angle_between(int x, int a, int b) {
b = modN(b - a);
x = modN(x - a);
if (b < 180) {
return x < b;
} else {
return b < x;
}
}
где (в случае проверки углов) modN()
будет реализовано как
// modN(x) is assumed to calculate Euclidean (=non-negative) x % N.
static int modN(int x) {
const int N = 360;
int m = x % N;
if (m < 0) {
m += N;
}
return m;
}
Ответ 3
void normalize( float& angle )
{
while ( angle < -180 ) angle += 360;
while ( angle > 180 ) angle -= 360;
}
bool isWithinRange( float testAngle, float a, float b )
{
a -= testAngle;
b -= testAngle;
normalize( a );
normalize( b );
if ( a * b >= 0 )
return false;
return fabs( a - b ) < 180;
}
Ответ 4
Если angle2 всегда 0, а angle1 всегда находились между 0 и 180, это было бы легко:
return angle1 < 180 && 0 < target && target < angle1;
если я правильно читаю требования.
Но это не так сложно добраться.
int reduced1 = (angle1 - angle2 + 360) % 360; // and imagine reduced2 = 0
if (180 < reduced1) { angle2 = angle1; reduced1 = 360 - reduced1; } // swap if backwards
int reducedTarget = (target - angle2 + 360) % 360;
return reduced1 < 180 && 0 < reducedTarget && reducedTarget < reduced1;
Ответ 5
Я делал это раньше, сравнивая углы.
![enter image description here]()
В эскизе выше вектор AD будет находиться между AB и AC, если и только если
angle BAD + angle CAD == angle BAC
Из-за неточностей с плавающей запятой я сравнил значения после округления их сначала, чтобы сказать 5 знаков после запятой.
Итак, дело сводится к тому, что алгоритм угла между двумя векторами p
и q
, который просто ставится как:
double a = p.DotProduct(q);
double b = p.Length() * q.Length();
return acos(a / b); // radians
Я оставлю вычисления DotProduct и Length в качестве упражнения для поиска Google. И вы получаете векторы просто путем вычитания координат одного терминала из другого.
Вы должны, конечно, сначала проверить, являются ли AB и AC параллельными или антипараллельными.
Ответ 6
Есть угол T между углами A и B, всегда есть два ответа: true и false.
Нам нужно указать, что мы имеем в виду, и в этом случае мы ищем нормализованные малые углы развертки и находится ли наш угол между этими значениями. При любых двух углах между ними существует рефлекторный угол, является нормированное значение T в пределах этого рефлекторного угла?
Если мы повернем A и B и T такие, что T = 0 и нормализуем A и B с точностью до + -многокруга (180 ° или 2PI). Тогда наш ответ: есть ли у А и В разные знаки и находятся в пределах полукруга между собой.
Если мы вычтем угол из теста, то добавим 180 ° (так что A относится к T + 180). Затем мы mod на 360, давая нам диапазон между [-360 °, 360 °], мы добавляем 360 ° и мод снова (обратите внимание, что вы также можете просто проверить, если он отрицательный и добавить 360, если это так), что дает нам значение, которое определенными как [0 °, 360 °]. Мы вычитаем 180 °, давая нам значение между [-180 °, 180 °] относительно T + 180 ° -180 ° aka, T. Таким образом, T теперь угол 0, и все углы попадают в нормализованный диапазон. Теперь мы проверяем, чтобы углы изменили знак и что они не более чем на 180 ° друг от друга, у нас есть наш ответ.
Поскольку вопрос задается в С++:
bool isAngleBetweenNormalizedSmallSweepRange(int test, int a, int b) {
int a_adjust = ((((a - test + 180)) % 360) + 360) % 360 - 180;
int b_adjust = ((((b - test + 180)) % 360) + 360) % 360 - 180;
return ((a_adjust ^ b_adjust) < 0) && ((a_adjust - b_adjust) < 180) && ((a_adjust - b_adjust) > -180);
}
Мы также можем сделать некоторые трюки, чтобы упростить код и избежать ненужных modulo ops (см. комментарии ниже). Normalize будет перемещать угол a в диапазон [-180 °, 180 °] относительно угла t.
int normalized(int a, int test) {
int n = a - test + 180;
if ((n > 360) || (n < -360)) n %= 360;
return (n > 0)? n - 180: n + 180;
}
bool isAngleBetweenNormalizedSmallSweepRange(int test, int a, int b) {
int a_adjust = normalized(a,test);
int b_adjust = normalized(b,test);
return ((a_adjust ^ b_adjust) < 0) &&
((a_adjust > b_adjust)? a_adjust-b_adjust: b_adjust-a_adjust) < 180;
}
Также, если мы можем быть уверены, что диапазон равен [0,360], мы можем выполнить более простой оператор if
bool isAngleBetweenNormalizedSmallSweepRange(int test, int a, int b) {
int dA = a - test + 180;
if (dA > 360) {
dA -= 360;
}
int a_adjust = (dA > 0) ? dA - 180 : dA + 180;
int dB = b - test + 180;
if (dB > 360) {
dB -= 360;
}
int b_adjust = (dB > 0) ? dB - 180 : dB + 180;
return ((a_adjust ^ b_adjust) < 0)
&& ((a_adjust > b_adjust) ? a_adjust - b_adjust : b_adjust - a_adjust) < 180;
}
JS Проверка скрипта кода
Ответ 7
Все верхние ответы здесь неправильные. Поэтому я чувствую, что мне необходимо отправить ответ.
Я просто просматриваю часть ответа, который я разместил здесь: fooobar.com/questions/348550/... Этот ответ также касается случая, когда вы уже знаете, какой угол является левая сторона и правая сторона рефлексивного угла. Но вам также нужно определить, какая сторона угла есть.
1 st чтобы найти самый левый угол, если любое из этих утверждений истинно angle1
- ваш левый угол:
-
angle1 <= angle2 && angle2 - angle1 <= PI
-
angle1 > angle2 && angle1 - angle2 >= PI
Для простоты скажем, что ваш левый угол равен l, а ваш правый угол - r, и вы пытаетесь найти, если g между ними.
Проблема здесь в том, что кажется. Есть по существу 3 положительных случая, которые мы ищем:
- l ≤ g ≤ r
- l ≤ g ∧ r < л
- g ≤ r ∧ r < л
Поскольку вы вычисляете левую и правую стороны угла, вы заметите, что здесь есть возможность оптимизации при одновременном выполнении обоих процессов. Ваша функция будет выглядеть так:
if(angle1 <= angle2) {
if(angle2 - angle1 <= PI) {
return angle1 <= target && target <= angle2;
} else {
return angle2 <= target || target <= angle1;
}
} else {
if(angle1 - angle2 <= PI) {
return angle2 <= target && target <= angle1;
} else {
return angle1 <= target || target <= angle2;
}
}
Или, если вам это нужно, вы можете перейти в это состояние кошмара:
angle1 <= angle2 ?
(angle2 - angle1 <= PI && angle1 <= target && target <= angle2) || (angle2 - angle1 > PI && (angle2 <= target || target <= angle1)) :
(angle1 - angle2 <= PI && angle2 <= target && target <= angle1) || (angle1 - angle2 > PI && (angle1 <= target || target <= angle2))
Обратите внимание, что вся эта математика предполагает, что ваш вход находится в радианах и в диапазоне [0: 2π].
Live Example
Ответ 8
Я нашел эту цитату из этот поток:
если точка P находится внутри треугольника ABC, то
Площадь PAB + Площадь PBC + Область PAC = Область ABC
заметим, что если P находится на краю AB, BC или CA, то это выполняется. Но эффективно, одна из областей PAB, PBC, PAC равна 0 (поэтому просто убедитесь, что вы убедитесь, что).
если P вне, указанное выше равенство НЕ выполняется...
Как определить область? у вас есть два варианта: 1) теорема Герона, включает sqrt, медленнее 2), тем более предпочтительным способом является перекрестная продукция (или, фактически, половина абсолютного значения (сумма продукты за вычетом суммы продуктов))
например, если A = (x1, y1) B = (x2, y2), C = (x3, y3) Площадь = абс (х1 * х2 + у2 * у3 + х3 * у1-х1 * у3-х3 * у2-х2 * у1)/2
также вы можете быть осторожны с ошибками с плавающей запятой... вместо проверки на строгое неравенство, проверьте абс (b-a)
Надеюсь, что это поможет
Ответ 9
Используя аналогичный стиль функции, как в вашем вопросе, мне повезло со следующими методами:
public static bool IsInsideRange(double testAngle, double startAngle, double endAngle)
{
var a1 = System.Math.Abs(AngleBetween(startAngle, testAngle));
var a2 = System.Math.Abs(AngleBetween(testAngle, endAngle));
var a3 = System.Math.Abs(AngleBetween(startAngle, endAngle));
return a1 + a2 == a3;
}
public static double AngleBetween(double start, double end)
{
return (end - start) % 360;
}
Ответ 10
Я знаю, что этот пост старый, но, похоже, не принят приемлемый ответ, и я нашел следующий подход достаточно надежным. Хотя это может быть больше, чем вам нужно. Он поддерживает угловые диапазоны более 180 градусов (а также более 360 градусов и отрицательные углы). Он также поддерживает десятичную точность.
Метод использует эту вспомогательную функцию normalize()
для преобразования углов в правое пространство:
float normalize( float degrees )
{
//-- Converts the specified angle to an angle between 0 and 360 degrees
float circleCount = (degrees / 360.0f);
degrees -= (int)circleCount * 360;
if( 0.0f > degrees )
{
degrees += 360.0f;
}
return degrees;
}
Здесь решение:
bool isWithinRange( float start, float end, float angle )
{
if( fabsf( end - start ) >= 360.0f )
{
//-- Ranges greater or equal to 360 degrees cover everything
return true;
}
//-- Put our angle between 0 and 360 degrees
float degrees = normalize( angle );
//-- Resolve degree value for the start angle; make sure it's
// smaller than our angle.
float startDegrees = normalize( start );
if( startDegrees > degrees )
{
startDegrees -= 360.0f;
}
//-- Resolve degree value for the end angle to be within the
// same 360 degree range as the start angle and make sure it
// comes after the start angle.
float endDegrees = normalize( end );
if( endDegrees < startDegrees )
{
endDegrees += 360.0f;
}
else if( (endDegrees - startDegrees) >= 360.0f )
{
endDegrees -= 360.0f;
}
//-- All that remains is to validate that our angle is between
// the start and the end.
if( (degrees < startDegrees) || (degrees > endDegrees) )
{
return false;
}
return true;
}
Надеюсь, это поможет кому-то.
Ответ 11
У вас есть угол a
и c
, и не видно, находится ли угол b
между этими углами.
Вы можете рассчитать угол между a->b
и a->c
. Если ∠a->c
меньше ∠a->b
, b
должно быть между a
и c
.
Расстояние между углами, a
и b
function distanceBetweenAngles(a, b) {
distance = b - a;
if (a > b) {
distance += 2*pi;
}
return distance;
}
Затем вы можете сделать
// Checks if angle 'x' is between angle 'a' and 'b'
function isAngleBetween(x, a, b) {
return distanceBetweenAngles(a, b) >= distanceBetweenAngles(a, x);
}
Это предполагает, что вы используете Radians, а не Degrees, как и нужно. Он удаляет много ненужного кода.
Ответ 12
Если у вас есть время, проверьте это:
bool AngleIsBetween(int firstAngle, int secondAngle, int targetAngle)
{
while (firstAngle >= 360)
firstAngle -= 360;
while (secondAngle >= 360)
secondAngle -= 360;
while (targetAngle >= 360)
targetAngle -=360;
while (firstAngle < 0)
firstAngle += 360;
while (secondAngle < 0)
secondAngle += 360;
while (targetAngle < 0)
targetAngle +=360;
int temp = secondAngle;
if (firstAngle > secondAngle)
{
secondAngle = firstAngle;
firstAngle = temp;
}
if ((secondAngle - firstAngle) > 180)
{
temp = secondAngle - 360;
secondAngle = firstAngle;
firstAngle = temp;
}
return ((targetAngle >= firstAngle) && (targetAngle <= secondAngle));
}
Измените параметры на float, если вам нужно.