// 检查是否可能发生圆事件,arc 为新增的弧 private void FixCircleEvent(ArcData arc) { if (arc == null || arc.Prev == null || arc.Next == null) { // 凑不齐三个焦点 return; } if (arc.CircleEvent != null) { arc.CircleEvent.Deleted = true; arc.CircleEvent = null; } Point a = arc.Prev.A; Point b = arc.A; Point c = arc.Next.A; Point center; // 圆心 Point bottom; // 圆最低点 // 一般来说,三点肯定可以算出一个圆,除非三点共线 if (GenerateCircle(a, b, c, out center, out bottom)) { // 并不是所有圆都作数,因为后期一个焦点并不只对应一段弧,可能被分割成好几段 // 只有当两个交点重合时,才当作是圆事件 // 这里可以去树里面找出两个交点的结点 // 也可以直接创建个新的直接算 var temp01 = new IntersectionData(a, b); var temp02 = new IntersectionData(b, c); // 重合是在扫描线扫到圆最低点时才发生,所以这里传的参数应该是最低点坐标 var l = temp01.CalcIntersection(bottom.Y); var r = temp02.CalcIntersection(bottom.Y); // 判断l、r 两点是否重合,这里应该是造成较大误差的地方,可以想办法改进判断方法 if (EqualTo(l, r)) { // 产生一个圆事件,加入优先队列 arc.CircleEvent = new CircleEvent(center, bottom, arc); priorityQueue.Push(arc.CircleEvent); } } }
private void Finish() { // 找出一条扫描线,保证抛物线交点一定在边框外 var y = XSize + YSize; // 先找到双向链表头 var curr = tree.FindMin(); // 遍历双向链表 while (curr != null) { if (curr.Next != null) { var intersection = new IntersectionData(curr.A, curr.Next.A); if (curr.S1 != null) { FinishSegment(curr.S1, intersection.CalcIntersection(y)); } } curr = curr.Next; } }
private void ProcSiteEvent(SiteEvent e) { if (tree.Empty()) { // 树为空时,直接添加成第一个结点 IData data = new ArcData(e.QueryPoint(), null, false); tree.AddFirstArc(data); } else { // 先找出站点正上方的弧 ArcData oldArc = tree.GetAboveArc(e.QueryPoint()); // 正上方的弧将被拆成两段,所以肯定会破坏其的圆事件 if (oldArc.CircleEvent != null) { oldArc.CircleEvent.Deleted = true; oldArc.CircleEvent = null; } //if (oldArc.A.Y == e.QueryPoint().Y) { // // 可以选择不要这个分支,直接统一走下面那个 // // 这样的话,计算交点的方法需要做特殊处理 // // 如果正上方弧的 y 坐标和新站点的 y 坐标一致 // // 则是一条弧分裂成两条弧,将原先的弧删掉,新增一个内部结点和两个叶子结点 // var intersection = new IntersectionData(oldArc.A, e.QueryPoint(), oldArc.Fa, oldArc.IsLeft(), tree); // var arc01 = new ArcData(oldArc.A, intersection, true); // var arc02 = new ArcData(e.QueryPoint(), intersection, false); // // 维护双向链表 // arc01.Prev = oldArc.Prev; // arc02.Prev = arc01; // if (oldArc.Next != null) { // oldArc.Next.Prev = arc02; // } // if (oldArc.Prev != null) { // oldArc.Prev.Next = arc01; // } // arc01.Next = arc02; // arc02.Next = oldArc.Next; // arc01.S0 = oldArc.S0; // // 交点目前坐标,用于创建维诺图边 // var p = intersection.CalcIntersection(e.QueryPoint().Y); // // 交点计算 // var seg = new Segment(p, QueryFace(e.QueryPoint()), QueryFace(oldArc.A)); // arc01.S1 = seg; // arc02.S0 = seg; // FixCircleEvent(arc01); //} else { // 正常情况下,正上方的弧会被新增弧分割成两段 // 将原先的弧删掉,新增两个内部结点和三个叶子结点 var intersection01 = new IntersectionData(oldArc.A, e.QueryPoint(), oldArc.Fa, oldArc.IsLeft(), tree); var intersection02 = new IntersectionData(e.QueryPoint(), oldArc.A, intersection01, false, tree); var arc01 = new ArcData(oldArc.A, intersection01, true); var arc02 = new ArcData(e.QueryPoint(), intersection02, true); var arc03 = new ArcData(oldArc.A, intersection02, false); // 维护双向链表 arc01.Prev = oldArc.Prev; arc02.Prev = arc01; arc03.Prev = arc02; if (oldArc.Next != null) { oldArc.Next.Prev = arc03; } if (oldArc.Prev != null) { oldArc.Prev.Next = arc01; } arc01.Next = arc02; arc02.Next = arc03; arc03.Next = oldArc.Next; // 原弧分裂出来的两段弧分别继承S0、S1 arc01.S0 = oldArc.S0; arc03.S1 = oldArc.S1; // 交点目前坐标,用于创建维诺图边 // 目前两个交点应该是重叠的,由这两个点勾勒出同一条维诺图边 var p = intersection01.CalcIntersection(e.QueryPoint().Y); var seg = new Segment(p, QueryFace(e.QueryPoint()), QueryFace(oldArc.A)); arc01.S1 = seg; arc02.S0 = seg; seg = new Segment(p, QueryFace(e.QueryPoint()), QueryFace(oldArc.A)); arc02.S1 = seg; arc03.S0 = seg; FixCircleEvent(arc01); FixCircleEvent(arc03); // } } }