IVl*_*lad 5 c# algorithm computational-geometry
我在 C# 中正确实现 Bentley-Ottmann 算法时遇到了一些麻烦。我试图根据这里的伪代码来实现它。我在下面发布了我的主要代码。假设 myBST
和PriorityQueue
classes 实现正确,您是否发现代码有任何问题?
没有错误,但不是所有的交点都找到了,只有一些。我的猜测是else
代码部分有错误(当前事件是交点时)。通过交换 BST 中两个段的位置,我不确定伪代码是什么意思。我这样做的方式好吗?因为最终,两者在 BST 中并没有真正交换。我也不能只是改变他们的位置,因为这可能会破坏 BST 属性。
另外,我是否正确假设段在 BSTY
中按其左端点的坐标排序?
我注意到的另一个我似乎无法追踪的错误是,有时要点(0, 0)
进入eventList
. 在没有交集的情况下(0, 0)
输出Geometry.Intersects
,但在这种情况下,if
条件应该阻止它被添加。我不知道它是如何进入的。如果我eventList
在添加一个点后打印 的内容,则(0, 0)
永远不会出现。如果我在提取和弹出元素后打印内容,(0, 0)
有时会出现。这可能与Pop()
混淆引用的方法有什么关系,还是我的PriorityQueue
实现中绝对有问题?
如果需要,我也可以展示我对 BST 和优先级队列的实现。
static class BentleyOttman
{
private static void AddIntersectionEvent(PriorityQueue eventList, Segment segEv, Segment segA, SegPoint i)
{
i.IntersectingSegments = new Tuple<Segment, Segment>(segEv, segA);
i.Type = SegmentPointType.IntersectionPoint;
eventList.Add(i);
}
public static void Solve(Panel surface, TextBox debug)
{
debug.Clear();
var segList = Generator.SegList;
PriorityQueue eventList = new PriorityQueue();
foreach (Segment s in segList)
{
eventList.Add(new SegPoint(s.A, s, SegmentPointType.LeftEndpoint));
eventList.Add(new SegPoint(s.B, s, SegmentPointType.RightEndpoint));
}
BST sweepLine = new BST();
while (!eventList.Empty)
{
SegPoint ev = eventList.Top();
eventList.Pop();
if (ev.Type == SegmentPointType.LeftEndpoint)
{
Segment segEv = ev.Segment;
sweepLine.Insert(segEv);
Segment segA = sweepLine.InorderPre(segEv);
Segment segB = sweepLine.InorderSuc(segEv);
SegPoint i = new SegPoint();
if (segA != null && Geometry.Intersects(segEv, segA, out i.Point))
{
AddIntersectionEvent(eventList, segA, segEv, i);
}
if (segB != null && Geometry.Intersects(segEv, segB, out i.Point))
{
AddIntersectionEvent(eventList, segEv, segB, i);
}
}
else if (ev.Type == SegmentPointType.RightEndpoint)
{
Segment segEv = ev.Segment;
Segment segA = sweepLine.InorderPre(segEv);
Segment segB = sweepLine.InorderSuc(segEv);
sweepLine.Remove(segEv);
SegPoint i = new SegPoint();
if (segA != null && segB != null && Geometry.Intersects(segA, segB, out i.Point))
{
AddIntersectionEvent(eventList, segA, segB, i);
}
}
else
{
Generator.DrawPoint(ev.Point, surface, Brushes.Red);
Segment seg1 = ev.IntersectingSegments.Item1;
Segment seg2 = ev.IntersectingSegments.Item2;
sweepLine.Remove(seg1);
sweepLine.Remove(seg2);
Segment t = new Segment(seg1);
seg1 = new Segment(seg2);
seg2 = new Segment(t);
sweepLine.Insert(seg1);
sweepLine.Insert(seg2);
Segment segA = sweepLine.InorderPre(seg2);
Segment segB = sweepLine.InorderSuc(seg1);
SegPoint i = new SegPoint();
if (segA != null && Geometry.Intersects(seg2, segA, out i.Point))
AddIntersectionEvent(eventList, segA, seg2, i);
if (segB != null && Geometry.Intersects(seg1, segB, out i.Point))
AddIntersectionEvent(eventList, seg1, segB, i);
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
如果不知道其他类到底做什么,我真的无法理解你的代码,但我可以回答你的一些其他问题。
这些线段在 BST 中按照它们与扫描线相交的 Y 坐标进行排序。因此,当我们遇到左端点时,我们使用输入线段左端点的 y 坐标(将其与其他线段与扫描线的交点的 Y 坐标进行比较)将该线段添加到树中。当我们遇到右端点时,我们从树中删除该段。当我们遇到交点时,两条线段与扫掠线的交点顺序就会改变,因此我们交换树中的两条线段。例如考虑两个部分
A = {(-1,1),(1,-1)} and
B = {(-1,-1),(1,1)}
Run Code Online (Sandbox Code Playgroud)
当扫描线的 X 坐标小于 0 时,线段 A 与扫描线的交点大于线段 B 与扫描线的交点。如果扫描线大于 0,则相反。(画一张图。)
画一个简单的例子,逐步跟踪正在发生的事情,为每个事件绘制扫描线,并在事件之间的列中标记分段,然后跟踪 BST 并验证 BST 是否保持与有效区域中的段的顺序相同。(如果这还不够清楚,我很抱歉。)
注意:这假设您的线段处于“一般位置”,即没有线段是垂直的,在给定点相交的线段不超过两个,等等。维基百科页面上概述了处理不处于一般位置的线段