2013-08-06 2 views
3

Я разрабатываю приложение, которое предполагает получение угла камеры в игре. Угол может быть где угодно от 0-359. 0 - Северный, 90 - Восток, 180 - Юг и т. Д. Я использую API, который имеет метод getAngle() в классе Camera.Определение среднего угла

Как бы найти среднее значение между различными углами камеры. Реальное среднее значение 0 и 359 составляет 179,5. Как угол камеры, это будет юг, но, очевидно, 0 и 359 оба очень близки к северу.

+3

Подсказка: в принципе, вы пытаетесь найти угол вектора, который представляет собой сумму векторов, представляющих ваши указания. – sashkello

+0

Является ли моя математика неправильной или 1-359 недостаточно для круга? – Greg

+1

На самом деле не имеет смысла говорить об «усредняющих» углах. Если у вас есть два угла, вы можете разделить дугу между ними, но усреднение 3 или более углов довольно бессмысленно, если они не находятся относительно близко друг к другу. –

ответ

5

Вы можете думать об этом с точки зрения векторов. Пусть θ1 и θ2 будут вашими двумя углами, выраженными в radians. Тогда мы можем определить, х и у компоненты единичных векторов, которые при таких углах:

 
x1 = sin(θ1) 
y1 = cos(θ1) 

x2 = sin(θ2) 
y2 = cos(θ2) 

Вы можете добавить эти два вектора, и определить е и у компонентов результата:

 
x* = x1 + x2 
y* = y1 + y2 

Наконец, вы можете определить угол этого результирующего вектора:

 
θavg = tan-1(y*/x*) 

или, еще лучше, использовать atan2 (функция поддерживается многими языками):

 
θavg = atan2(y*, x*) 

Вы, вероятно, придется отдельно обрабатывать случаи, когда y* = 0иx* = 0, так как это означает, что два вектора указывают в точно противоположных направлениях (так что должен «средний» быть?).

+0

+1, это самый «матовый» способ решить проблему, но вы не обсуждаете, что делать, когда знаменатель равен 0. – jxh

+0

@jxh Mathy, true. Но если вы делаете графическую анимацию, вы должны стараться избегать трансцендентных функций. Они занимают много раз столько же времени, сколько простая арифметика, и это действительно все, что вам нужно для решения этой проблемы. – Gene

+0

@Gene: «Простая» часть арифметики не так проста при обобщении решения более чем на один угол, как задал ОП. – jxh

0

Это зависит от того, что вы подразумеваете под «средним». Но нормальное определение - это биссектриса включенного острого угла. Вы должны положить оба в пределах 180 градусов друг от друга. Существует много способов сделать это, но простой - увеличить или уменьшить один из углов. Если углы a и b, то это будет делать:

if (a < b) 
    while (abs(a - b) > 180) a = a + 360 
else 
    while (abs(a - b) > 180) a = a - 360 

Теперь вы можете вычислить среднее арифметическое:

avg = (a + b)/2 

Конечно, вы можете захотеть, чтобы нормализовать еще раз:

while (avg < 0) avg = avg + 360 
while (avg >= 360) avg = avg - 360 

На вашем примере у вас будет a = 0, b = 359. Первый цикл увеличит до 360. Среднее будет 359,5. Конечно, вы можете округлить это до целого числа, если хотите. Если вы округлите до 360, тогда конечный набор петель уменьшится до 0.

Обратите внимание, что если ваши углы всегда нормализованы до [0..360], ни одна из этих циклов никогда не выполняется более одного раза. Но они, вероятно, являются хорошей практикой, так что дикий аргумент не приводит к сбою вашего кода.

0

Вы хотите делить пополам углы, которые не усредняют их. Сначала найдите расстояние между ними, возьмите кратчайший путь, затем разделите его пополам и добавьте к одному из углов. Например:

A = 355 
B = 5 

if (abs(A - B) < 180) { 
    Distance = abs(A - B) 
    if (A < B) { 
     Bisect = A + Distance/2 
    } 
    else { 
     Bisect = B + Distance/2 
    } 
} 
else { 
    Distance = 360 - abs(A - B) 
    if (A < B) { 
     Bisect = A - Distance/2 
    } 
    else { 
     Bisect = B - Distance/2 
    } 
} 

Или что-то в этом роде - «Bisect» должен выходить на ноль для данных входов. Есть, вероятно, умные способы, чтобы арифметика выходила с меньшими операциями if и abs.

+0

(комментарий Сашкелло ранее - еще один общий способ взглянуть на одно и то же - вычислить векторную сумму векторов, представленных углами, и вычислить угол этого вектора. Это будет работать более чем на 2 угла.) –

0

В комментарии вы упомянули, что все «углы», которые необходимо усреднить, находятся в пределах 90 градусов друг к другу. Я предполагаю, что на самом деле есть только одна камера, но она много перемещается, и вы создаете какой-то механизм стабильности изображения для камеры POV.

В любом случае существует только специальный случай, когда камера может находиться в квадранте 270-359 и в квадранте 0-89. Для всех остальных случаев вы можете просто взять простой средний показатель. Таким образом, вам просто нужно обнаружить этот особый случай, и когда это произойдет, обработайте углы в квадранте 270-359 вместо -90 -1. Затем, после вычисления простого среднего, при необходимости отрегулируйте его обратно в сектор 270-359.

В коде C:

int quadrant (int a) { 
    assert(0 <= a && a < 360); 
    return a/90; 
} 

double avg_rays (int rays[], int num) { 
    int i; 
    int quads[4] = { 0, 0, 0, 0 }; 
    double sum = 0; 
    /* trivial case */ 
    if (num == 1) return rays[0]; 
    for (i = 0; i < num; ++i) ++quads[quadrant(rays[i])]; 
    if (quads[0] == 0 || quads[3] == 0) { 
     /* simple case */ 
     for (i = 0; i < num; ++i) sum += rays[i]; 
     return sum/num; 
    } 
    /* special case */ 
    for (i = 0; i < num; ++i) { 
     if (quadrant(rays[i]) == 3) rays[i] -= 360; 
     sum += rays[i]; 
    } 
    return sum/num + (sum < 0) * 360; 
} 

Этот код может быть оптимизирован за счет ясности цели. Когда вы обнаруживаете особый случай, вы можете исправить sum после факта. Итак, вы можете вычислить sum и разобраться в специальном футляре и сделать исправление за один проход.

double avg_rays_opt (int rays[], int num) { 
    int i; 
    int quads[4] = { 0, 0, 0, 0 }; 
    double sum = 0; 
    /* trivial case */ 
    if (num == 1) return rays[0]; 
    for (i = 0; i < num; ++i) { 
     ++quads[quadrant(rays[i])]; 
     sum += rays[i]; 
    } 
    if (quads[0] == 0 || quads[3] == 0) { 
     /* simple case */ 
     return sum/num; 
    } 
    /* special case */ 
    sum -= quads[3]*360; 
    return sum/num + (sum < 0) * 360; 
} 

Я уверен, что он может быть дополнительно оптимизирован, но он должен дать вам начало.

Смежные вопросы