#математика #геометрия #вектор
Есть стандартная формула расчета угла между векторами: И вот есть два вектора, угол между которыми никак не вычислить, так как правая часть уравнения меньше -1. Вот эти вектора: var x1 = -0.045797169475341334, y1 = -0.9989507591808752; var x2 = 0.04579716947534099, y2 = 0.9989507591808753; В итоге, выражение: (x1 * x2 + y1 * y2) / Math.sqrt(Math.pow(x1, 2) + Math.pow(y1, 2)) * Math.sqrt(Math.pow(x2, 2) + Math.pow(y2, 2)) дает результат: -1.0000000000000002 И если взять арккосинус этого числа, то будет NaN, что и понятно, так как он определен на промежутке от -1 до 1. Как мне скорректировать формулу, чтобы этой ошибки не было?
Ответы
Ответ 1
Варианта решения проблемы имеются как минимум 2. Оба они требует, чтобы оба вектора имели ненулевую длину, но для вектора с нулевой длиной сама постановка вопроса о каком-либо угле не вполне корректна, и как Вам обрабатывать такую ситуацию, Вам должно быть виднее. 1) вычисляем промежуточное значение: r = (x1 * x2 + y1 * y2) / Math.sqrt(Math.pow(x1, 2) + Math.pow(y1, 2)) * Math.sqrt(Math.pow(x2, 2) + Math.pow(y2, 2)) Проверяем на попадание r в диапазон [-1, 1]: if (r < -1) r = -1; if (r > 1) r = 1; Вычисляем арккосинус. Так убираются ошибки округления, "выбивающие" r из диапазона. 2) Пользуемся функцией atan2 phi = Math.atan2(y2, x2) - Math.atan2(y1, x1); При необходимости, приводим полученный угол в нужный диапазон (0-180град или -90 - +90) Я бы написал так: if (phi < 0) phi += Math.PI * 2; if (phi > Math.Pi) phi -= Math.PI; Оба алгоритма выдадут ошибку при x1=y1=0 или x2=y2=0, о чем написано выше. Вариант с atan2, имхо предпочтительней, т.к., например при x1=y1=1e+10, x2=y2=1e-10 точность вычислений по первому варианту будет околонулевой (e10 взято просто для примера, м.б. нужно существенно больше). Математические функции указаны для Java, для .NET названия содержат заглавную букву: Math.Sqrt, Math.Atan2 и т. д.Ответ 2
Если есть желание воспользоваться формулой для разности углов (а оно понятно из вопроса), то можно использовать тангенсы половинного аргумента с периодом 360: phi = 2(Math.atan2(y2, x2+r2) - Math.atan2(y1, x1+r1)); Проверим эту формулу на данных x1 = sqrt(3), y1=1, x2= - sqrt(3), y2=1 r1=r2=2: Правильный ответ: cos(fi) = -1/2, fi=120. По формуле для разности: fi1=2arctg(1/(2+sqrt(3))) = 30, fi2=2arctg(1/(2-sqrt(3))=150, fi=fi2-fi1 = 120. Остаётся только преобразовать интервал, на выбор: либо mod(fi,360), либо mod(fi+180,360)-180 В то же время: Модуль нужно убрать, потому что его нет в теории. Тангенсы в чистом виде лучше не использовать, потому что есть риск ошибки. Так, для тех же данных по формуле phi = (Math.atan2(y2, x2) - Math.atan2(y1, x1); имеем: fi = arctg(sqrt(-3)) - arctg(sqrt(3)) = -120. Парадокс объясняется тем, что тангенс имеет период 180, а сos(fi+180) = cos(fi)cos(180)+sin(fi)sin(180) = - cos(fi).
Комментариев нет:
Отправить комментарий