Сортировка массива точек по часовой стрелке

16

Существует ли такой алгоритм сортировки массива 2D точек по часовой стрелке?
Я конкретно имею дело с прямоугольным треугольником в моем случае, поэтому только 3 балла.

Однако мне интересно знать, существует ли такой алгоритм, если нет, то каков простой способ вернуть 3 точки моего треугольника по часовой стрелке?

Редактировать: я пытаюсь вычислить точки по часовой стрелке относительно центра тяжести многоугольника, который является выпуклым.

Обновление: это реализация, которую я в конечном итоге использовал, основываясь на выбранном ответе, она не критична для производительности и происходит только время от времени, поэтому она работает.

ArrayList<PVector> pointList = new ArrayList<PVector>();
pointList.add(A);
pointList.add(B);
pointList.add(C);
Collections.sort( pointList, new TriangleVectorComparator(origin) );

return pointList;

// Comparator
package triangleeditor;

import java.util.Comparator;

import processing.core.PVector;

public class TriangleVectorComparator implements Comparator<PVector>  {
    private PVector M; 
    public TriangleVectorComparator(PVector origin) {
        M = origin;
    }

    public int compare(PVector o1, PVector o2) {
        double angle1 = Math.atan2(o1.y - M.y, o1.x - M.x);
        double angle2 = Math.atan2(o2.y - M.y, o2.x - M.x);

        //For counter-clockwise, just reverse the signs of the return values
        if(angle1 < angle2) return 1;
        else if (angle2 < angle1) return -1;
        return 0;
    }

}
onedayitwillmake
источник
1
вернуть можно вернуть angle1 <angle2? 1: угол2> угол1? -1: 0;
ademar111190
2
Это может быть выражено многими способами, но я стараюсь избегать вложенных тернарных операторов, особенно когда даю примеры.
onedayitwillmake
@onedayitwillmake Не могли бы вы переместить код, который вы использовали в ответ? Это действительно не относится к вопросу, но это очень ценно для будущих читателей.
Анко
@ Анко, я думаю, что вы правы, но в то же время я думаю, что людям будет проще найти это таким образом. Это также помогает удостовериться, что я не убираю ответ Самхочевара, который просто основан на моем.
onedayitwillmake

Ответы:

19

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

по часовой стрелке или против часовой стрелки

В вашем случае, я считаю, что использование барицентра точек в качестве ориентира целесообразно.

Хорошим методом для неизвестного количества точек может быть следующий:

  • позволять P[0], P[1], ... P[n-1] быть список точек для сортировки
  • пусть M будет барицентром всех точек
  • вычислить так a[0], a[1], ... a[n-1], чтобыa[i] = atan2(P[i].y - M.y, P[i].x - M.x);
  • сортировать точки относительно их a значения, используя, qsortнапример.

Однако вы можете быть уверены, что хороший алгоритм сортировки будет работать плохо с тремя входными значениями по сравнению со специальным методом. Использование atan2все еще действует, но просто не используйте qsort.

Сэм Хоцевар
источник
Это сработало отлично :)
onedayitwillmake
1
У этого метода есть имя?
onedayitwillmake
1
Я считаю, что это просто называется «сортировка по полярному углу». Например, это компонент сканирования Грэма .
Сэм Хочевар
Хит производительности qsortздесь крошечный по сравнению с atan2.
Небольшое предупреждение: это может привести к сбою и сгоранию (в зависимости от используемого языка программирования и библиотек), если какая-либо из точек окажется точно в барицентре (или любой другой точке ориентации, которую вы используете). Возможно, вы захотите исключить такие точки между вторым и третьим шагом.
Мартин Сойка
3

Я считаю, что то, о чем вы на самом деле спрашиваете здесь, это порядок наматывания треугольника, который на самом деле довольно просто проверить.

Поскольку в вашем треугольнике есть только три точки, ваш треугольник уже находится в порядке по часовой стрелке или против часовой стрелки, и поэтому все, что вам нужно сделать, это проверить, какой из этих двух он является, и изменить порядок индексов при намотке. не тот, который вы хотите.

Вот общая идея, предполагая, что три вершины треугольника - это a , b и c , и что у вас есть простая операция вычитания вектора:

Vector2 AToB = b - a;
Vector2 BToC = c - b;
float crossz = AToB.x * BToC.y - AToB.y * BToC.x;
if ( crossz > 0.0f )
{
  // clockwise
}
else
{
  // counter-clockwise.  Need to reverse the order of our vertices.
}

Обратите внимание, что в зависимости от того, каким образом вы ориентировали ось + y (вверх или вниз), случаи «по часовой стрелке» и «против часовой стрелки» могут быть изменены на противоположные тому, как я их пометил в комментариях в этом примере кода.

Тревор Пауэлл
источник
Мне нужно передать эти точки в Box2D (реализация Java), чтобы создать многоугольник. хотя это не то, о чем я просил, очень хорошее понимание :)
onedayitwillmake
2

Можете ли вы дать больше информации? Вам нужен порядок точек CCW, но какой пункт должен быть центром порядка?

Если у вас есть только треугольник (3 точки) на плоскости, вы можете вычислить определитель из матрицы, где линии - это координаты точек (3-я координата равна 1). Если определитель> 0, точки расположены в порядке CCW. Если нет, вы можете использовать, например, последние два пункта, и вы получите заказ CCW.

Если у вас есть точки A, B, C, то ваша матрица выглядит так:

|xA, yA, 1|
|xB, yB, 1|
|xC, yC, 1|

Определитель: xA * yB + xB * yC + xC * yA - yB * xC - yC * xA - yA * xB. Тогда вы можете сравнить это с нуля. Если это> 0, вернуть точки A, B, C, если нет, вернуть A, C, B.

Если у вас есть набор точек и вы знаете, что они образуют выпуклый многоугольник (все являются частью выпуклой оболочки) и хотите получить их порядок, вы можете использовать сканирование Грэма или марш Джарвиса (это алгоритмы для поиска выпуклой оболочки из многих точек, но здесь тоже должно работать :))

zacharmarz
источник
чтобы завершить то, что сказал zacharmarz, если вы просто хотите отсортировать ваши точки по часовой стрелке вокруг определенной точки, вы можете создать массив из пар с плавающей точкой и точки, в которой вы сохраняете все точки с их остаточным углом относительно этой конкретной точки, а затем сортируете этот массив.
Ali1S232