eryar

PipeCAD - Plant Piping Design Software.
RvmTranslator - Translate AVEVA RVM to OBJ, glTF, etc.
posts - 603, comments - 590, trackbacks - 0, articles - 0

性能提升-空间二叉查找树

Posted on 2023-08-06 18:53 eryar 阅读(604) 评论(0)  编辑 收藏 引用 所属分类: 2.OpenCASCADE

性能提升-空间二叉查找树

eryar@163.com

Abstract.  OpenCASCADE provides NCollection_UBTree to achieve high performance search overlapped boxes. The algorithm of unbalanced binary tree of overlapped bounding boxes. Once the tree of boxes  of geometric objects is constructed, the algorithm is capable of fast geometric selection of objects.  The tree can be easily updated by adding to it a new object with bounding box. The time of adding to the tree  of one object is O(log(N)), where N is the total number of  objects, so the time  of building a tree of  N objects is O(N(log(N)). The search time of one object is O(log(N)). Defining  various classes  inheriting NCollection_UBTree::Selector  we can perform various kinds of selection over the same b-tree object.

Key Words. Unbalanced Binary Tree, Binary Search Tree, Binary Sort Tree, Bounding Box

1 Introduction

非平衡二叉树(Unbalanced Binary Tree)又叫二叉查找树(Binary Search Tree)或二叉排序树(Binary Sort Tree)。它的定义很简单,就是左子树上所有节点的值都要小于根节点上的值。右子树上所有节点值都要大于根节点上的值。在二叉查找树上执行操作时间与树的高度成正比。对于一棵含有n个结点的完全二叉树,这些操作的最坏情况运行时间为O(lg(n))。但是如果树是含n个结点的线性链,则这些操作的最坏的情况运行时间为O(n)。一棵随机构造的二叉查找树的期望高度为O(lg(n)),从而这种树上操作的平均时间为O(lg(n))。

几何搜索(geometry searching)大致分两类:一类是区域搜索问题(range searching problem),另一类是点的定位问题(point location problem)。区域搜索问题要回答的是给定一个区域,看有多少模型属于这个区域。当然,我们可以对所有模型进行遍历,这种算法时间复杂度为O(N),效率不高。常见的高效的区域搜索算法有k-D树,k-D树就是一种多维的平衡二叉树。还有比较常见的KNN问题,这些都是计算几何处理的问题。

OpenCASCADE中提供一种空间查找二叉树算法NCollection_UBTree,字面意思是非平衡二叉树Unbalanced Binary Tree。把上图中的数字换成包围盒,构造二叉查找树。为了解决查找二叉树单链问题,加入随机处理,可以使查找性能达到O(log(N)),相对普通遍历速度而言还是不错的。本文结合示例代码说明如何使用这个非平衡二叉树。

2 Example

在OpenCASCADE中有多个函数来实现将很多无序边Edges连接成Wire,需要查询一条边Edge的一个顶点Vertex在一定精度范围内相连的顶点Vertex有哪些?

首先,实现一个选择类,通过选择类来进行过滤:

typedef NCollection_UBTree<Standard_Integer, Bnd_Box> BoxTree;
typedef NCollection_UBTreeFiller<Standard_Integer, Bnd_Box> BoxTreeFiller;
class BoxSelector : public BoxTree::Selector
{
public:
    BoxSelector(const TColgp_SequenceOfPnt& thePoints, Standard_Real theTolerance)
        : Selector()
        , myPoints(thePoints)
        , myTolerance(theTolerance)
    {
    }
    virtual Standard_Boolean Reject(const Bnd_Box& theBox) const
    {
        return theBox.IsOut(myBox);
    }
    virtual Standard_Boolean Accept(const Standard_Integer& theIndex)
    {
        if (theIndex > myPoints.Size() || theIndex == myIndex)
        {
            return Standard_False;
        }
        const gp_Pnt& aPnt = myPoints.Value(theIndex);
        if (aPnt.SquareDistance(myPnt) < myTolerance)
        {
            myResultIndex.Append(theIndex);
            return Standard_True;
        }
        return Standard_False;
    }
    void SetCurrentPoint(const gp_Pnt& thePnt, Standard_Integer theIndex)
    {
        myPnt = thePnt;
        myBox.Add(thePnt);
        myIndex = theIndex;
    }
    const TColStd_ListOfInteger& GetResultIndex() const
    {
        return myResultIndex;
    }
    void ClearResultIndex()
    {
        myResultIndex.Clear();
    }
protected:
private:
    const TColgp_SequenceOfPnt& myPoints;
    gp_Pnt myPnt;
    Bnd_Box myBox;
    Standard_Integer myIndex;
    Standard_Real myTolerance;
    TColStd_ListOfInteger myResultIndex;
};

主要实现两个抽象函数Reject()和Accept(),以及设置当前选择器的状态。Reject()函数用来判断要查找的Box与当前空间范围的状态,如果在外,则返回True。当两个Box有相交时,会调用Accept()函数,在此函数中判断两个点的距离是否在容差范围内,若在容差范围内,则将点记录起来。主函数main代码如下:

int main(int argc, char* argv[])
{
    // Fill tree with random points.
    BoxTree aBoxTree;
    BoxTreeFiller aTreeFiler(aBoxTree);
    math_BullardGenerator aRandom;
    TColgp_SequenceOfPnt aPoints;
    for (Standard_Integer i = 1; i <= 100; ++i)
    {
        gp_Pnt aPnt(aRandom.NextReal(), aRandom.NextReal(), aRandom.NextReal());
        aPoints.Append(aPnt);
        Bnd_Box aBox;
        aBox.Add(aPnt);
        aTreeFiler.Add(i, aBox);
    }
    aTreeFiler.Fill();
    // Query points near the given point.
    BoxSelector aSelector(aPoints, 0.1);
    for (Standard_Integer i = aPoints.Lower(); i <= aPoints.Upper(); ++i)
    {
        const gp_Pnt& aPnt = aPoints.Value(i);
        aSelector.SetCurrentPoint(aPnt, i);
        Standard_Integer aSize = aBoxTree.Select(aSelector);
        if (aSize > 0)
        {
            std::cout << "Search Point : " << aPnt.X() << " \t " << aPnt.Y() << " \t " << aPnt.Z() << std::endl;
            const TColStd_ListOfInteger& aResult = aSelector.GetResultIndex();
            for (TColStd_ListOfInteger::Iterator aIt(aResult); aIt.More(); aIt.Next())
            {
                const gp_Pnt& aPoint = aPoints.Value(aIt.Value());
                std::cout << "Target Point : " << aPoint.X() << " \t " << aPoint.Y() << " \t " << aPoint.Z() << std::endl;
            }
            std::cout << "=============================" << std::endl;
        }
        aSelector.ClearResultIndex();
    }
    return 0;
}

先用随机函数随机生成100个点,并将点通过BoxTreeFiller添加到查找树aBoxTree中,调用Fill函数构造查找树。

再使用类BoxSelector来进行快速查找,查找之前先设置当前点及包围盒。然后调用aBoxTree.Select(aSelector)进行查找。

3 Conclusion

类NCollection_UBTree通过构造包围盒的非平衡二叉树来加快区域搜索速度。如何提高搜索速度,是计算几何处理的范畴。在OpenCASCADE中这个类使用场景比较多,如将无序边构造成Wire时都用这个类:BRepLib_MakeWire::Add(const TopTools_ListOfShape& L), ShapeAnalysis_FreeBounds::ConnectEdgesToWires()。包括后面引入的BVH都是为了提高搜索速度,在合适的场景中多使用这些算法,会对程序性能的提升有很大帮助。

 


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理