Если вам не нужен фактический евклидов угол, но что-то, что вы можете использовать в качестве основы для сравнения углов, тогда выбор геометрии такси может быть выбором, потому что вы можете отбросить тригонометрию и ее медленность при поддержании ТОЧНОСТИ ( или, по крайней мере, с очень незначительной потерей точности, см. ниже).
В основных современных браузерных движках коэффициент ускорения составляет от 1,44 до 15,2, а точность почти такая же, как в atan2. Расчет угла алмаза в среднем в 5,01 раза быстрее, чем atan2, а при использовании встроенного кода в Firefox 18 ускорение достигает коэффициента 15,2. Сравнение скорости: http://jsperf.com/diamond-angle-vs-atan2/2.
Код очень прост:
function DiamondAngle(y, x)
{
if (y >= 0)
return (x >= 0 ? y/(x+y) : 1-x/(-x+y));
else
return (x < 0 ? 2-y/(-x-y) : 3+x/(x-y));
}
Приведенный выше код дает вам угол между 0 и 4, а atan2 - угол между -PI и PI, как показано в следующей таблице:
Обратите внимание, что угол ромба всегда положителен и находится в диапазоне 0-4, в то время как atan2 дает также отрицательные радианы. Таким образом, угол алмаза более нормализован. И еще одно замечание: atan2 дает немного более точный результат, потому что длина диапазона равна 2 * pi (т.е. 6,283185307179586), тогда как в углах алмаза она равна 4. На практике это не очень важно, например. Рад 2.3000000000000001 и 2.3000000000000002 имеют оба угла ромба 1.4718731421442295, но если мы снизим точность, опустив один ноль, то рад 2.300000000000001 и 2.300000000000002 дают оба угла ромба. Эта «потеря точности» в углах алмаза настолько мала, что оказывает существенное влияние, только если расстояния огромны. Вы можете играть с конверсиями в http://jsbin.com/bewodonase/1/edit?output (Старая версия: http://jsbin.com/idoyon/1):
Приведенного выше кода достаточно для быстрого сравнения углов, но во многих случаях необходимо преобразовать угол ромба в радианы и наоборот. Если вы, например. иметь некоторый допуск в виде радиальных углов, а затем у вас есть петля в 100 000 раз, где этот допуск сравнивается с другими углами, сравнивать с использованием atan2 нецелесообразно. Вместо этого перед выполнением цикла вы изменяете допуск в радианах на допуск таксика (алмазные углы) и выполняете внутриконтурные сравнения с использованием допусков алмаза, и таким образом вам не нужно использовать медленные тригонометрические функции в критических по скорости частях кода (= в петли).
Код, который делает это преобразование:
function RadiansToDiamondAngle(rad)
{
var P = {"x": Math.cos(rad), "y": Math.sin(rad) };
return DiamondAngle(P.y, P.x);
}
Как вы заметили, есть cos
и sin
. Как вы знаете, они медленные, но вам не нужно делать преобразование в цикле, но перед циклом и ускорение огромно.
И если по какой-то причине вам нужно преобразовать угол бриллианта в радианы, например. после зацикливания и сравнения углов для возврата, например. минимальный угол сравнения или что-либо в радианах, код выглядит следующим образом:
function DiamondAngleToRadians(dia)
{
var P = DiamondAngleToPoint(dia);
return Math.atan2(P.y,P.x);
}
function DiamondAngleToPoint(dia)
{
return {"x": (dia < 2 ? 1-dia : dia-3),
"y": (dia < 3 ? ((dia > 1) ? 2-dia : dia) : dia-4)};
}
Здесь вы используете atan2
, что медленно, но идея в том, чтобы использовать это вне любых циклов. Вы не можете преобразовать угол алмаза в радианы, просто умножив его на некоторый коэффициент, но вместо этого найдите точку в геометрии такси, у которой угол ромба между этой точкой и положительной осью X является рассматриваемым углом алмаза, и конвертируйте эту точку в радианы, используя atan2.
Этого должно быть достаточно для быстрого сравнения углов.
Конечно, есть другие методы ускорения atan2 (например, CORDIC и справочные таблицы), но AFAIK все они теряют точность и все же могут быть медленнее, чем atan2.
ПРЕДПОСЫЛКИ: Я проверил несколько методов: точечные произведения, внутренние произведения, закон косинуса, единичные окружности, справочные таблицы и т. Д., Но ничего не было достаточно в случае, когда важны как скорость, так и точность. Наконец я нашел страницу в http://www.freesteel.co.uk/wpblog/2009/06/encoding-2d-angles-without-trigonometry/, в которой были нужные функции и принципы.
Сначала я предположил, что для точного и быстрого сравнения расстояний можно использовать также расстояния такси, потому что большее расстояние в евклидово больше и в такси. Я понял, что вопреки евклидовым расстояниям угол между начальной и конечной точкой влияет на расстояние такси. Только длины вертикальных и горизонтальных векторов можно легко и быстро преобразовать между евклидовым и таксиским, но в любом другом случае необходимо учитывать угол, и тогда процесс идет слишком медленно (?).
Итак, в заключение я думаю, что в приложениях, критичных к скорости, где есть цикл или рекурсия нескольких сравнений углов и / или расстояний, углы быстрее сравнивать в пространстве такси и расстояниях в евклидовом (в квадрате, без использования sqrt) пространство.