2016-12-23 3 views
2

Недостаток здесь в классе Android VelocityTracker: если ваша скорость в направлении X превышает максимальную частоту, она изменяется равной maxVelocity и то же самое для Y. Но это означает, что если мы идем под углом 20 ° и со скоростью 200, а наша максимальная частота равна 20. Наша скорость изменяется на 20 * sqrt (2) под углом 45 °. Правильный ответ заключается в масштабировании mXVelocity и mYVeloicity соотношением фактической скорости и maxVelocity.Выше maxVelocity меняет направление на диагональ, обходное решение

Мой вопрос: мне нужно прибегнуть к двум квадратным корням, чтобы исправить эту ошибку?

Скорость - это направление и скорость объекта. Изменение направления из-за достижения максимальной скорости должно считаться дефектом. Это также явно вызывает недостаток в том, что диагональная скорость быстрее ортогональной.

Проблематика бит кода сродни:

mXVelocity = accumX < 0.0f ? Math.max(accumX, -maxVelocity) : Math.min(accumX, maxVelocity); 
mYVelocity = accumY < 0.0f ? Math.max(accumY, -maxVelocity) : Math.min(accumY, maxVelocity); 

Чтобы обойти эту проблему я использую:

tracker.computeCurrentVelocity(units); //maxVelocity 
double velocityX = tracker.getXVelocity(); 
double velocityY = tracker.getYVelocity(); 
double actualVelocitySq = velocityX * velocityX + velocityY * velocityY; 
double maxVelocitySq = maxVelocity * maxVelocity; 
if (actualVelocitySq > maxVelocitySq) { 
    //double excessFactor = Math.sqrt(maxVelocitySq)/Math.sqrt(actualVelocitySq); 
    double excessFactor = Math.sqrt(maxVelocitySq/actualVelocitySq); //leewz's optimization 
    velocityX *= excessFactor; 
    velocityY *= excessFactor; 
} 

Есть ли какой-нибудь способ сделать избежать двойного квадратного корня? Или какая-то другая вещь, которая в противном случае исправляет эту жуткую ошибку?


Update:

Ответ, как представляется, масштаб обоих компонентов в соответствии с одним компонентом, который превышает максимальную скорость на наиболее. Это не строго масштабирование в соответствии с фактической скоростью, но оно фиксирует основную часть проблемы с помощью простой математики.

double scale; 
double vectorX = Math.abs(velocityX); 
double vectorY = Math.abs(velocityY); 
if (vectorX > maxVelocity) { 
    scale = maxVelocity/vectorX; 
    if (vectorY > maxVelocity) { 
     scale = Math.min(scale, maxVelocity/vectorY); 
    } 
    velocityX *= scale; 
    velocityY *= scale; 
} else { 
    if (vectorY > maxVelocity) { 
     scale = maxVelocity/vectorY; 
     velocityX *= scale; 
     velocityY *= scale; 
    } 
} 
+0

Примечание для читателей: Код с дефектом в вопросе находится в 'VelocityTracker.computeCurrentVelocity (единиц, maxVelocity) Android,', а не кода Аскера. – leewz

ответ

1

Вы можете отрезать Math.sqrt(maxVelocitySq), потому что вы знаете maxVelocitySq = maxVelocity * maxVelocity. Даже без этого, вы можете использовать один sqrt(), выполнив деление первым:

double excessFactor = Math.sqrt(maxVelocitySq/actualVelocitySq); 

Математически, я считаю, принимая требуется квадратный корень, но как вы берете квадратный корень по-прежнему открыт для вас. В частности, Fast Inverse Square Root работает для вашего удобства. Вот код, используя fisr().

if (actualVelocitySq > maxVelocitySq) { 
    double excessFactor = fisr(actualVelocitySq/maxVelocitySq); 
    velocityX *= excessFactor; 
    velocityY *= excessFactor; 
} 

Here's a Java implementation of FISR.

Оговорка: FISR был разработан для старшего поколения процессоров Intel, которые были медленными при операциях с плавающей точкой (особенно деление, которое до сих пор использует приведенный выше код), а не JIT'd виртуальная машина (таких как Java), работающих на ARM (общая архитектура для Android). Не забудьте прокомментировать ваш код, чтобы узнать, является ли стоимость квадратного корня достаточно значительной для оптимизации, а затем для определения того, дает ли FISR значительное улучшение.

+0

Клянусь, я запустил номера и не мог сделать деление первым, но, по-видимому, не так, как это очевидно. Просто проверил графики и да, должно быть, сделал это неправильно вручную. Я клянусь, что есть какая-то векторная математика, которая может эффективно избежать квадратов в тех случаях, когда я думал, что они должны быть нужны. Хотя угадывание и проверка снова закончились бы алгоритмом с квадратным корнем. Поэтому, если есть какой-то способ решить результат величины без решения для величины, это может потребовать такие вещи. – Tatarize

+0

1. Возможно, вы использовали целочисленное деление в своих тестах. 2. Способ нормализации без sqrt означает, что FISR не должен существовать. Вы можете избежать этого в случаях, когда вектор умножается на его длину. 3. Вы уверены, что квадратный корень дорог? Вычислительная скорость уже стоит дорого. 4. Не догадывайтесь и проверяйте, но приближение Ньютона. Пользовательский sqrt может делать меньше итераций, чем встроенный sqrt. – leewz

+0

Целью было бы не нормализовать в теории. Но, приходите к тому же общему ответу, что нормализация даст вам. Даже если вы обманули и нормализовались с maxX и maxY до maxVelocity. Так как треугольники x, y, v конгруэнтны с X, Y, V. Если мы масштабируем в соответствии с maxX или maxY, а не V, мы можем просто масштабироваться в соответствии с максимальным требуемым коэффициентом масштабирования компонента. И примените этот максимум к другому. Таким образом, оставляя X или Y при maxVelocity, но не изменяя фактический вектор, без использования квадратного корня в любом месте. – Tatarize

0

Трюк состоит в том, чтобы не масштабироваться в соответствии с величиной фактического вектора, а скорее компонентом, который превысил требование на наибольшую сумму. Затем масштабируйте как x, так и y. Это не требует более сложной математики, чем разделения. И вообще не пугает угол вектора. В то время как результат maxVelocity мог, под углом, превышать фактическую скорость в квадрате (2). Он лучше соответствует документации о том, что делает maxVelocity.

maxVelocity float: максимальная скорость, которую можно вычислить этим методом . Это значение должно быть объявлено в том же блоке, что и параметр . Это значение должно быть положительным.

double scale; 
double vectorX = Math.abs(velocityX); 
double vectorY = Math.abs(velocityY); 
if (vectorX > maxVelocity) { 
    scale = maxVelocity/vectorX; 
    if (vectorY > maxVelocity) { 
     scale = Math.min(scale, maxVelocity/vectorY); 
    } 
    velocityX *= scale; 
    velocityY *= scale; 
} else { 
    if (vectorY > maxVelocity) { 
     scale = maxVelocity/vectorY; 
     velocityX *= scale; 
     velocityY *= scale; 
    } 
}