Сортировка набора трехмерных точек по часовой стрелке / против часовой стрелки

В трехмерном пространстве у меня есть неупорядоченный набор, скажем, из 6 точек; что-то вроде этого:

           (A)*
                          (C)*
(E)*
                         (F)*
     (B)*

                  (D)*

Точки образуют трехмерный контур, но не упорядочены. Под неупорядоченными я подразумеваю, что они хранятся в

unorderedList = [A - B - C - D - E - F]

Я просто хочу реорганизовать этот список, начиная с произвольного места (скажем, точки A) и перемещая точки по часовой стрелке или против часовой стрелки. Что-то вроде этого:

orderedList = [A - E - B - D - F - C]

or

orderedList = [A - C - F - D - B - E]

Я пытаюсь реализовать алгоритм как можно проще, поскольку упомянутый набор точек соответствует N-кольцевой окрестности каждой вершины на сетке из ~ 420000 точек, и я должен сделать это для каждой точки на сетке. .

Некоторое время назад было аналогичное обсуждение по пунктам в 2 -D, но пока мне непонятно, как перейти от этого подхода к моему трехмерному сценарию.


person CodificandoBits    schedule 30.07.2011    source источник


Ответы (2)


Понятия «по часовой стрелке» или «против часовой стрелки» не имеют четкого определения без оси и ориентации! (Доказательство: что, если бы вы посмотрели на эти точки с другой стороны экрана монитора или, например, перевернули их!)

Вы должны определить ось и ориентацию и указать их в качестве дополнительных входных данных. Способы указать это включают:

  • линия (1x=2y=3z), используя правило правой руки
  • (единичный) вектор (A_x, A_y, A_z), используя правило правой руки; это предпочтительный способ сделать это

Чтобы определить ориентацию, вы должны глубже взглянуть на свою проблему: вы должны определить размер сетки «вверх» и «вниз». Затем для каждого набора точек вы должны взять центроид (или другую «внутреннюю» точку) и построить единичный вектор, указывающий «вверх», перпендикулярный поверхности. (Один из способов сделать это - найти плоскость по методу наименьших квадратов, а затем найти два перпендикулярных вектора, проходящих через эту точку, выбрав один в направлении «вверх».)


Вам нужно будет использовать любое из приведенных выше предложений, чтобы определить вашу ось. Это позволит вам переформулировать вашу проблему следующим образом:

Входы:

  • набор точек {P_i}
  • ось, которую мы будем называть «осью z» и рассматривать как единичный вектор с центром на центроиде (или где-то «внутри») точек.
  • ориентация (например, против часовой стрелки), выбранная одним из вышеуказанных методов

Настраивать:

  • Для всех точек выберите два взаимно ортогональных единичных вектора к оси, которые мы будем называть «осью y» и «осью x». (Просто поверните единичный вектор оси z на 90 градусов в двух направлениях, http://en.wikipedia.org/wiki/Rotation_matrix#Basic_rotations)

Алгоритм:

  • Для каждой точки P спроецируйте P на ось x и ось y (используя точечное произведение), затем используйте http://en.wikipedia.org/wiki/Atan2

Когда у вас есть углы, вы можете просто отсортировать их.

person ninjagecko    schedule 30.07.2011
comment
Все хорошо, кроме atan2, в плоскости надо просто сравнивать точки с помощью векторного произведения. - person unkulunkulu; 30.07.2011
comment
Нет ничего плохого в использовании atan2. Тем не менее, предложение ункулункулу интересно! Обычно (P1 x P2) · Z дает вам непоследовательный порядок, но если вы объедините его с правильной техникой сортировки, такой как быстрая сортировка или сортировка на основе сводной таблицы, это сработает. Это связано с тем, что перекрестное произведение на круге говорит, что быстрее достичь цели, двигаясь CW или CCW? Другие алгоритмы сортировки могут дать сбой. В противном случае использование перекрестного произведения затруднено; например, если вы попытаетесь отсортировать по (X x P2) · Z, sin не обратится в диапазоне от 0 до 180 градусов! Также нужно быть осторожным с нормализацией, как обычно. - person ninjagecko; 30.07.2011
comment
ninjagecko, я думаю, что предложенный вами подход (проецирование точек x, y, z на наиболее подходящую плоскость) кажется адекватным в моем случае. Однако я думаю о гипотетической проблеме: предположим, что моя наиболее подходящая плоскость - это z = 0 (нормальный: 0,0,1), и что две из моих точек, которые будут проецироваться, имеют одинаковые координаты x и y (отличаются только по координате z). В этом случае проекция из 3-D в 2-D будет выглядеть так, как если бы это была всего одна точка! Я прав? Я что-то упускаю? Если это так, как решить эту проблему? - person CodificandoBits; 30.07.2011
comment
@ Мигель: извините, я не удосужился заявить об этом. Это равносильно тому, чтобы спросить, что мне делать в моем алгоритме сортировки, если два значения совпадают? Ответ таков, и вы должны относиться к ним так же. Если вас это беспокоит, вы всегда можете выбрать другую ось. Аналогичная проблема возникает при определении того, является ли (0,1) 0 градусами или 360 градусами; это произвольно из-за характера вашей заявленной проблемы. В вашем случае я не ожидаю, что это вас беспокоит, поскольку ваши баллы, вероятно, были генерированы непересекающимся образом в местном районе. - person ninjagecko; 30.07.2011

Я не могу подтвердить эффективность этого кода, но он работает, и вы можете оптимизировать его части по мере необходимости, я просто не умею этого.
Код находится на C #, с использованием классов системной коллекции и linq .
Vector3 - это класс с плавающей запятой x, y, z и статические векторные математические функции.
Node - это класс с переменной Vector3 с именем pos

//Sort nodes with positions in 3d space.
//Assuming the points form a convex shape.
//Assuming points are on a single plain (or close to it).

public List<Node> sortVerticies( Vector3 normal, List<Node> nodes ) {

    Vector3 first = nodes[0].pos;

    //Sort by distance from random point to get 2 adjacent points.
    List<Node> temp = nodes.OrderBy(n => Vector3.Distance(n.pos, first ) ).ToList();

    //Create a vector from the 2 adjacent points,
    //this will be used to sort all points, except the first, by the angle to this vector.
    //Since the shape is convex, angle will not exceed 180 degrees, resulting in a proper sort.
    Vector3 refrenceVec = (temp[1].pos - first);

    //Sort by angle to reference, but we are still missing the first one.
    List<Node> results = temp.Skip(1).OrderBy(n => Vector3.Angle(refrenceVec,n.pos - first)).ToList();

    //insert the first one, at index 0.
    results.Insert(0,nodes[0]);

    //Now that it is sorted, we check if we got the direction right, if we didn't we reverse the list.
    //We compare the given normal and the cross product of the first 3 point.
    //If the magnitude of the sum of the normal and cross product is less than Sqrt(2) then then there is more than 90 between them.
    if ( (Vector3.Cross( results[1].pos-results[0].pos, results[2].pos - results[0].pos ).normalized + normal.normalized).magnitude < 1.414f ) {
        results.Reverse();
    }

    return results;
}
person Max Izrin    schedule 25.02.2017