Как “привязать” вектор направления (2D) к компасу (N, NE, E, SE, S, SW, W, NW)?
-
08-07-2019 - |
Вопрос
У меня есть куча векторов, перпендикулярных поверхностям окон в программном обеспечении для 3D-моделирования.Спроецированный на плоскость XY, я хотел бы знать, в каком направлении они обращены, переведенный на плоскость 8 координаты по компасу (Север, Северо-Восток, Восток, Юго-Восток, Юг, Юго-Запад, Запад и Северо-Запад).
Векторы работают следующим образом:
- ось X представляет Восток-Запад (при положительном значении "Восток").
- ось y представляет Север-Юг (где Север является положительным).
- таким образом
- (0, 1) == Север
- (1, 0) == Восток
- (0,-1) == Юг
- (-1,0) == Запад
Учитывая вектор (x, y), я ищу ближайшую из 8 координат компаса.Есть какие-нибудь идеи о том, как сделать это элегантно?
Решение
Это работает в Java, вычисляя значение 0...7 для восьми направлений:
import static java.lang.Math.*;
int compass = (((int) round(atan2(y, x) / (2 * PI / 8))) + 8) % 8;
Результат отображается на компасе следующим образом:
0 => E
1 => NE
2 => N
3 => NW
4 => W
5 => SW
6 => S
7 => SE
Другие советы
Я бы, наверное, просто позвонил атан2() чтобы определить угол курса («рыскание»), а затем использовать последовательность if
:s или некоторые математические вычисления, чтобы «привязать» его к углу, кратному 90 градусам.
нет необходимости выполнять функцию atan.
если вы это сделаете:y / x вы получите наклон линии.Судя по полученному числу, вы можете определить угол / октант.
для положительных значений x (x>0)
- (y/x) > 2,4 -=> 90 градусов (север)
- 2.4 > (y/x) > 0.4 -=> 45 градусов (северо-запад)
- 0,4 > (y/x) > -0,4 -=> 0 градусов (к западу)
- -0,4 > (y/x) > -2,4 -=> -45 градусов (юго-запад)
- -2.4 > (y/x) -=> 90 градусов (южная)
и аналогичный список для отрицательных крестиков
и, наконец, исключительные случаи:
- (x==0 && y>0) -=> -90 градусов (южная)
- (x==0 && y<0) -=> 90 градусов (южная)
добавление:Я сообщаю об этом методе только в тех случаях, когда вычисление atan невозможно (например, во встроенной системе))
Мне пришлось немного покопаться.Вот высокооптимизированная процедура, которую я использую (используется в мобильных играх).
входные данные:x1, y1 = начальная точка вектора x2, y2 = конечная точка вектора выходные данные (0-7) = 0 = север, 1 = северо-запад, 2 = запад, ... и т.д.
int CalcDir( int x1, int y1, int x2, int y2 )
{
int dx = x2 - x1, dy = y2 - y1;
int adx = (dx<0)?-dx:dx, ady = (dy<0)?-dy:dy, r;
r=(dy>0?4:0)+(dx>0?2:0)+(adx>ady?1:0);
r=(int []){2,3,1,0,5,4,6,7}[r];
return r;
}
void CalcDirTest(){
int t = CalcDir(0, 0, 10, 1);
printf("t = %d",t);
t = CalcDir(0, 0, 9, 10);
printf("t = %d",t);
t = CalcDir(0, 0, -1, 10);
printf("t = %d",t);
t = CalcDir(0, 0, -10, 9);
printf("t = %d",t);
t = CalcDir(0, 0, -10, -1);
printf("t = %d",t);
t = CalcDir(0, 0, -9, -10);
printf("t = %d",t);
t = CalcDir(0, 0, 1, -10);
printf("t = %d",t);
t = CalcDir(0, 0, 10, -9);
printf("t = %d",t);
}
Это приведет к следующему результату:
t = 7
t = 6
t = 5
t = 4
t = 3
t = 2
t = 1
t = 0
(Векторы для теста могут выглядеть странно выбранными, но я немного изменил их все, чтобы они были четко в одном октанте, а не на точной границе)
Этот не использует atan2 и выполняет в худшем случае 4 сравнения и 2 продукта за вызов.Сравнивая x с y в 4 внутренних блоках (я редактировал только в первом блоке), можно сократить ровно до 4 сравнений и 1 продукта на вызов.
int compass(double x,double y)
{
double t = 0.392699082; // tan(M_PI/8.0);
if (x>=0)
{
if (y>=0)
{
if (x>y) { if (y<t*x) return E_COMPASS; }
else { if (x<t*y) return N_COMPASS; }
return NE_COMPASS;
}
else
{
if (-y<t*x) return E_COMPASS;
if (x<-t*y) return S_COMPASS;
return SE_COMPASS;
}
}
else
{
if (y>=0)
{
if (y<-t*x) return W_COMPASS;
if (-x<t*y) return N_COMPASS;
return NW_COMPASS;
}
else
{
if (-y<-t*x) return W_COMPASS;
if (-x<-t*y) return S_COMPASS;
return SW_COMPASS;
}
}
return E_COMPASS;
}