按顺时针/逆时针顺序对一组三维点进行排序

时间:2011-07-30 03:29:50

标签: algorithm sorting geometry mesh

在三维空间中,我有一组无序的,比如6分;像这样的东西:

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

                  (D)*

这些点形成三维轮廓,但它们是无序的。对于无序,我的意思是它们存储在

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

我只想从任意位置开始重新组织此列表(比如说A点)并顺时针或逆时针遍历点。像这样:

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

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

我正在尝试尽可能简单地实现算法,因为提到的点集对应于~420000点的网格上的每个顶点的N环邻域,并且我必须为每个点执行此操作在网格上。

前段时间有关于2-D点数的similar discussion,但目前我不清楚如何从这种方法转向我的3D场景。

2 个答案:

答案 0 :(得分:7)

没有轴和方向,“顺时针”或“逆时针”的概念没有明确定义! (证据:例如,如果你从显示器屏幕的另一侧观察这些点,或者将它们翻转,会怎样!)

您必须定义轴和方向,并将其指定为附加输入。指定方法包括:

  • 一条线(1x=2y=3z),使用右手规则
  • a(单位)向量(A_x, A_y, A_z),使用右手规则;这是这样做的首选方式

为了确定方向,您必须更深入地研究您的问题:您必须定义网格的“向上”和“向下”大小。然后,对于每组点,您必须采用质心(或另一个“内部”点)并构造一个指向“向上”的单位向量,该向量与表面垂直。 (这样做的一种方法是找到最小二乘拟合平面,然后通过该点找到两个垂直向量,在“向上”方向上选择一个。)


您需要使用上述任何建议来确定您的轴。这将允许您按如下方式重新表述您的问题:

输入:

  • 点数集{P_i}
  • 一个轴,我们将其称为“z轴”并将其视为以点的质心(或“内部”的某处)为中心的单位矢量
  • 通过上述方法之一选择的方向(例如逆时针方向)

设定:

算法:

一旦你有角度,你可以对它们进行排序。

答案 1 :(得分:1)

我无法证明此代码的效率,但它可以工作,你可以根据需要优化它的一部分,我只是不擅长它。
代码在C#中,使用系统集合类和linq Vector3是一个具有浮点数x,y,z和静态向量数学函数的类 Node是一个名为 pos

的Vector3变量的类
//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;
}