如何对点对进行排序以便它们连接?

时间:2013-11-24 20:00:48

标签: ruby geometry

我有一对矢量数组,它们是围绕多边形的顺时针路径的所有边。问题是,它们的顺序不正确。我想对它们进行排序,以便Array的开始和结束是相同的Vector,并且每个单独的点重叠(端点到起点):

require 'matrix'
unsorted_points = [[Vector[-5, 0], Vector[-3, 2]], 
                   [Vector[-3, 2], Vector[3, 2]], 
                   [Vector[-3, -2], Vector[-5, 0]], 
                   [Vector[3, 2], Vector[5, 0]], 
                   [Vector[3, -2], Vector[-3, -2]], 
                   [Vector[5, 0], Vector[3, -2]]]

sorted_points = [[Vector[-5, 0], Vector[-3, 2]], 
                 [Vector[-3, 2], Vector[3, 2]],
                 [Vector[3, 2], Vector[5, 0]],
                 [Vector[5, 0], Vector[3, -2]],
                 [Vector[3, -2], Vector[-3, -2]],
                 [Vector[-3, -2], Vector[-5, 0]]]

最常用的Ruby方法是什么?

编辑:向量是来自“矩阵”库的对象,但它们可以像数组一样被索引,例如unsorted_points[0][0][0]-5

2 个答案:

答案 0 :(得分:1)

首先将原始点对数组转换为第一个点为键的映射,第二个值为:

ptsMap = Hash[ * unsorted_points.flatten(1) ]

=> {Vector[-5, 0]  => Vector[-3, 2],
    Vector[-3, 2]  => Vector[3, 2],
    Vector[-3, -2] => Vector[-5, 0],
    Vector[3, 2]   => Vector[5, 0],
    Vector[3, -2]  => Vector[-3, -2],
    Vector[5, 0]   => Vector[3, -2] }

然后从第一个点开始,从第二个点到第一个点通过地图链(我注意你的数组不包含“点”,它包含两点之间的边):

ordered_edges = (1...unsorted_points.size).inject ( [unsorted_points.first] ) do |acc,_|
  nextPt = acc.last[1]
  acc << [ nextPt, ptsMap[nextPt] ]
end

=> [[Vector[-5, 0],  Vector[-3, 2]],
    [Vector[-3, 2],  Vector[3, 2]],
    [Vector[3, 2],   Vector[5, 0]],
    [Vector[5, 0],   Vector[3, -2]],
    [Vector[3, -2],  Vector[-3, -2]],
    [Vector[-3, -2], Vector[-5, 0]] ]

请注意,在此上下文中点不具有严格的可比性,因此没有总排序(根据简单sort的要求。每个点对都描述了一个提供部分排序的边。上面找到了候选总排序假设第一个边的头是你想要的第一个点。

如果原始列表实际上没有描述一组离散连接点,则此技术将失败。使用topological sorting可能会获得更强大的结果。 Ruby有一个库:TSort。通过这种方式,您可以检测到原始边缘集合何时不是单个强连接组件。

答案 1 :(得分:0)

[编辑:我最初提出了两种方法,一种是使用排序,另一种是递归。 @dbenhur表明没有sort可以工作,因为没有完整的数组元素排序。因此我删除了这种方法。为了记录,我误入歧途的核心是:

 unsorted.sort {|v1,v2| (v1 == first || v1[1]==v2[0]) ? -1 : 1}

第二种方法是使用递归。经过反思,很明显我可以在一个简单的循环中做同样的事情,接下来。]

sorted = [unsorted_points.shift]
until unsorted_points.empty?
  node = sorted[-1][1]
  target = unsorted_points.find {|e| e[0] == node}
  sorted << unsorted_points.delete(target)
end
sorted
  # => [[Vector[-5,  0], Vector[-3,  2]],
        [Vector[-3,  2], Vector[ 3,  2]],
        [Vector[ 3,  2], Vector[ 5,  0]],
        [Vector[ 5,  0], Vector[ 3, -2]],
        [Vector[ 3, -2], Vector[-3, -2]],
        [Vector[-3, -2], Vector[-5,  0]]]