Я знаю, что этот вопрос довольно старый, и принятый ответ помог мне в этом разобраться, но я думаю, что у меня есть более элегантное решение, которое также охватывает равенство (поэтому возвращает -1 для lowerThan, 0 для equals и 1 для большееThan).
Он основан на делении плоскости на две половины, одну от положительной оси отсчета (включительно) до отрицательной оси отсчета (исключая), а другая является ее дополнением.
Внутри каждой половины сравнение может быть выполнено по правилу правой руки (знак перекрестного произведения) или, другими словами, по знаку синуса угла между двумя векторами.Если 2 точки получены из разных половин, то сравнение является тривиальным и проводится между самими половинами.
Для достаточно равномерного распределения этот тест должен выполнить в среднем 4 сравнения, 1 вычитание и 1 умножение,кроме 4 вычитаний, сделанных с помощью ref, которые, по моему мнению, должны быть рассчитаны заранее.
int compareAngles(Point const & A, Point const & B, Point const & ref = Point(0,0)) {
typedef decltype(Point::x) T; // for generality. this would not appear in real code.
const T sinA = A.y - ref.y; // |A-ref|.sin(angle between A and positive ref-axis)
const T sinB = B.y - ref.y; // |B-ref|.sin(angle between B and positive ref-axis)
const T cosA = A.x - ref.x; // |A-ref|.cos(angle between A and positive ref-axis)
const T cosB = B.x - ref.x; // |B-ref|.cos(angle between B and positive ref-axis)
bool hA = ( (sinA < 0) || ((sinA == 0) && (cosA < 0)) ); // 0 for [0,180). 1 for [180,360).
bool hB = ( (sinB < 0) || ((sinB == 0) && (cosB < 0)) ); // 0 for [0,180). 1 for [180,360).
if (hA == hB) {
// |A-ref|.|B-ref|.sin(angle going from (B-ref) to (A-ref))
T sinBA = sinA * cosB - sinB * cosA;
// if T is int, or return value is changed to T, it can be just "return sinBA;"
return ((sinBA > 0) ? 1 : ((sinBA < 0) ? (-1) : 0));
}
return (hA - hB);
}