点定位:如何拆分更新梯形图和二分搜索结构 · (二):处理P和Q(Update trapezoidal map and search structure in point location)

1. 处理P(线段左端点)

1.1 处理思路

我们在总思路分析里面已经讲过处理的方法了,这里给大家总结归纳一下:

1)无论是横跨一个或多个梯形,我们都把原梯形拆分成左右部分(左梯形保留为原梯形,即它的指针不改变),然后将右梯形传递给下一个事件处理方法。

2)如果横跨一个梯形,则传给Q事件方法处理,反之横跨多个梯形,则传给S事件方法处理。

3)如果发生Pi和之前线段端点重合的情况,我们不拆分原梯形,直接将其传给下一个事件处理方法。

我们可以把对应的拆分更新思路归纳成下图:

在这里插入图片描述

1.2 代码解析

首先我们先来处理P事件方法的总体代码:

/**
 * handle p when adding a new segment,
 * this operation will partition
 * the original trapezoid into two parts ( vertical separation, left separation ):
 *              |
 * left(origin) | right
 *              |
 *
 * and pass the right into handleQ or handleS
 * */

public static
SearchVertex handleP( SearchVertex d, Line line, Stack<SearchVertex> de ) {
    assert d.type == SearchVertex.NodeType.TRAPEZOID;
    Vector p = line.startPoint;
    Vector q = line.endPoint;
    assert Vectors.sortByX( p, q ) <= 0;

    Trapezoid left = d.trapezoid;
    SearchVertex XNodeP = null;
    // normal case: p -> s or p -> q
    if ( !left.leftP.equals( p ) ) {
        // left separation
        Trapezoid right = new Trapezoid( p, left.rightP, left.top, left.bottom );
        Trapezoid.connectRights( right, left );
        left.rightP = p;

        // initialize the x node of P
        XNodeP = new SearchVertex( SearchVertex.NodeType.X_POINT_P, p, line );
        XNodeP.left = d;
        assert d.trapezoid.check();
        XNodeP.right = de == null ? handleQ( right, line, null ) : handleS( right, line, de );

        redirectParents( XNodeP, d );
        return XNodeP;
    }

    // degenerate case, p(x) -> s, or p(x) -> q
    // the left endPoint of the new segment and
    // the old one( the one already added into the map)
    // is the same
    XNodeP = de == null ? handleQ( left, line, null ) : handleS( left, line, de );
    redirectParents( XNodeP, d );
    return XNodeP;
}

我们先判断是不是Pi的退化情况,即Pi是不是已经存在于SS中:

if ( !left.leftP.equals( p ) )

如果是,我们直接将原梯形传到给下一个事件处理方法,并在最后返回这个事件处理得到的结点:

// degenerate case, p(x) -> s, or p(x) -> q
// the left endPoint of the new segment and
// the old one( the one already added into the map)
// is the same
XNodeP = de == null ? handleQ( left, line, null ) : handleS( left, line, de );
redirectParents( XNodeP, d );
return XNodeP;

如果不是,那先将原梯形左拆分:

// left separation
Trapezoid right = new Trapezoid( p, left.rightP, left.top, left.bottom );
Trapezoid.connectRights( right, left );
left.rightP = p;

之后创建一个X-Node存储Pi,设置其左孩子为左梯形(原梯形的指针),并递归处理右梯形,将它传给下一个事件处理方法:

// initialize the x node of P
XNodeP = new SearchVertex( SearchVertex.NodeType.X_POINT_P, p, line );
XNodeP.left = d;
assert d.trapezoid.check();
XNodeP.right = de == null ? handleQ( right, line, null ) : handleS( right, line, de );

最后将原梯形结点的父亲全部设置为新结点的父亲,所有父亲都包含这个新结点作为其中一个孩子:

redirectParents( XNodeP, d );

为高效地完成父子结点的重定向,我们规定叶结点存储其父亲结点的数据成员,也就是说,在叶结点层级,父子之间双向指向对方,不是父亲单向指向孩子,大家可以在SearchVertex类中找到这个数据成员:

public class SearchVertex extends Vertex {
    // have parent pointer point to its parent nodes,
    // use list, public final List<Vertex> neighbours in class Vertex,
    // since may have more than one parents; merge vertex with fewer parents
    public final List<Vertex> parents = super.neighbours;
    
    // other code
}

2. 处理Q(线段右端点)

2.1 处理思路

处理完P端点之后,就是处理Q端点,这里的处理思路和P是对称的:处理Q端点,我们把原梯形保留为右梯形,把左梯形传递给后面的S事件处理方法。同样,我们给到思路归纳的图例:

在这里插入图片描述

2.2 代码解析

同样,处理Q事件的代码逻辑和P刚好对称,所以这里我们递归处理左梯形:

/**
 * handle q when adding a new segment,
 * this operation will partition
 * the original trapezoid into two parts( vertical separation, right separation ):
 *      |
 * left | right(origin)
 *      |
 *
 * and pass the left into handleQ or handleS
 * */

// This method is only called when the segment crosses multiple trapezoids
// 这个公用方法是横跨多个梯形时,调用的
public static
    void handleQ( SearchVertex d, Line line, Stack<SearchVertex> de ) {
    SearchVertex newer = handleQ( d.trapezoid, line, de );
    redirectParents( newer, d );
}

// code to do the separation and update
// 具体的拆分代码
static
SearchVertex handleQ( Trapezoid right, Line line, Stack<SearchVertex> de ) {
    Vector p = line.startPoint;
    Vector q = line.endPoint;
    assert Vectors.sortByX( p, q ) <= 0;

    SearchVertex XNodeQ = null;
    // normal case: q -> s
    if ( !right.rightP.equals( q ) ) {
        // right separation
        Trapezoid middle = new Trapezoid( p, q, right.top, right.bottom );
        Trapezoid.connectLefts( middle, right );
        right.leftP = q;

        // initialize the x node of Q
        XNodeQ = new SearchVertex( SearchVertex.NodeType.X_POINT_Q, q, line );
        XNodeQ.left = handleS( middle, line, de );
        XNodeQ.right = new SearchVertex( right );
        XNodeQ.right.parents.add( XNodeQ );
        assert XNodeQ.right.trapezoid.check();

        return XNodeQ;
    }

    // degenerate case, q(x) -> s
    // the right endPoint of the new segment and
    // the old one( the one already added into the map)
    // is the same
    XNodeQ = handleS( right, line, de );
    return XNodeQ;
}

接下来就剩下处理S事件,我们把这个内容留到下一节进行讲解。


上一节:(一):总思路分析

下一节:(三):处理S

3. 参考资料

  1. Computational Geometry: Algorithms and Applications
  2. 计算几何 ⎯⎯ 算法与应用, 邓俊辉译,清华大学出版社

4. 免责声明

※ 本文之中如有错误和不准确的地方,欢迎大家指正哒~
※ 此项目仅用于学习交流,请不要用于任何形式的商用用途,谢谢呢;


在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值