在Lua中的 Bentley-Ottmann算法

4

我正在使用Lua实现Bentley-Ottmann算法,用于在多边形中查找相交点,使用的伪代码在这里

由于我是实现算法的新手,所以无法理解其中所有部分。以下是我的代码:

local function getPolygonIntersectingVertices( poly )
  -- initializing and sorting X
  local X = {}
  for i = 1, table.getn( poly ) do
    if i == 1 then
      table.insert( X, { x = poly[i].x, y = poly[i].y, endpoint = 'left' } )
    elseif i == table.getn( poly ) then
      table.insert( X, { x = poly[i].x, y = poly[i].y, endpoint = 'right' } )
    else
      table.insert( X, { x = poly[i].x, y = poly[i].y, endpoint = 'right' })
      table.insert( X, { x = poly[i].x, y = poly[i].y, endpoint = 'left' })
    end
  end

  local sortxy = function( a, b )
    if a.x < b.x then return true
    elseif a.x > b.x then return false
    elseif a.y <= b.y then return true
    else return false end
  end
  table.sort( X, sortxy )

  -- Main loop
  local SL = {}
  local L = {}
  local E
  local i = 1
  while next(X) ~= nil do
    E = { x = X[i].x, y = X[i].y, endpoint = X[i].endpoint }
    if E.endpoint == 'left' then
      -- left endpoint code here
    elseif E.endpoint == 'right' then
      -- right endpoint code here
    else
    end
    table.remove( X, i )
  end

  return L
end

我的多边形是一个使用以下结构的表格:{ { x = 1, y = 3 }, { x = 5, y = 6 }, ... }。
我该如何确定SL中“segE上面的线段”和“segE下面的线段”,如果扫描线(SL)为空怎么办?此外,在将I插入X时,应该用endpoint = 'intersect'标记它并将其附加到末尾,以便当循环到达此部分时进入主循环的“else”语句,还是我的整个算法都错了?
如果有人能向我展示一个Python、Ruby等简单实现的链接,那就太棒了,因为我发现很难跟随伪代码并将其与C++示例匹配。

你的代码甚至无法编译。其中有几个基本语法错误(函数的有趣前置声明,sortxy 函数中缺少 then,同一函数中缺少 end,使用不存在的运算符 != 而不是 ~=)。在寻求帮助之前,你应该先学习一些 Lua 的知识。 - prapin
1
prapin,我没有测试它,因为还没有什么可以测试的。我的问题更加普遍。当我有东西需要测试时,我会修复一些小问题,这只是我快速编写的东西,以展示我的进展并更容易地解释我的问题。不管怎样,还是谢谢。 - btatarov
好的,我已经修复了错误,现在代码可以编译了,只是为了让那些真正能够帮助解决问题的人回答这个问题。 - btatarov
2
这段代码是为哪个Lua版本编写的?table.getn早已被弃用。 - Alexander Gladysh
这是一个Python中可用的Bentley-Ottmann实现,您可能有兴趣将其用作参考:http://stackoverflow.com/a/33199826/432509 - ideasman42
1个回答

2
您的参考链接从我的位置无法访问。我将引用维基百科文章,它相当不错。
算法需要一个按y键排序的当前扫描线交点的BST,即按垂直顺序排序。因此,上面的线段是BST的后继,下面的线段是BST的前驱。在BST中找到给定节点的前驱和后继是标准操作。关键字K的前驱是K左侧最右边的节点。后继是K右侧最左边的节点。有几种计算方法。最简单的方法是使用父指针从K向上和向下遍历树。基于堆栈的迭代器是另一种方法。
如果扫描线(SL)为空怎么办?
继续处理事件队列。空扫描线只意味着没有线段在其当前x位置交叉。
此外,在将I插入X时,我应该将其标记为端点=“相交”,并将其附加到末尾吗?
事件队列必须按点的 x 坐标排序。当你插入一个交点时,它也必须按照 x 坐标顺序插入。它必须被标记为交点,因为交点与端点的处理方式不同。当它是按照 x 坐标顺序排列的第一个剩余项时,它将在适当的时候被处理。
请注意,Bentley Ottman - 就像几乎所有几何算法一样 - 由于浮点数不精确而容易出现可怕的失败。此外,该算法通常假定“一般位置”,这就排除了所有垂直边缘、点-边缘重合、边缘-边缘重叠等恶劣情况。我最强烈的建议是使用有理算术。即使这样,获得完全健壮、正确的实现也是一个重要的成就。从免费实现的数量非常少就可以看出这一点!

谢谢晚回复。我最终自己理解了。 :) - btatarov
我赞同使用有理数算术。我们尝试了各种技巧来使浮点数工作,但是在成千上万条边的交集集合中,由于舍入误差总会出现失败情况。使用有理数算术虽然速度较慢,但始终是正确的。 - bradgonesurfing

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接