zoukankan      html  css  js  c++  java
  • PaperImpl- Fast Software for Box Intersections

    PaperImpl- Fast Software for Box Intersections

    论文阅读参见:https://www.cnblogs.com/grass-and-moon/p/13344913.html

    CGAL的实现参见:https://www.cnblogs.com/grass-and-moon/p/13353521.html

    CGAL实现的使用参见:https://www.cnblogs.com/grass-and-moon/p/13219926.html

    完整的代码实现,如下,具体的每个步骤的理解,可以参见上面的论文阅读以及CGAL的实现浏览

    此处需要注意的是,BoxIntersection传入的迭代器需要是对应指针的迭代器

    #include <iostream>
    #include <limits>
    #include <vector>
    #include <functional>
    #include <algorithm>
    #include <unordered_map>
    
    #include "OpenMesh/Core/Mesh/TriMesh_ArrayKernelT.hh"
    #include "OpenMesh/Core/IO/MeshIO.hh"
    
    typedef OpenMesh::TriMesh_ArrayKernelT<> MyMesh;
    
    struct Box;
    typedef std::unordered_map<std::string, const Box*> MapInterBox;
    
    const double inf = -std::numeric_limits<double>::max();
    const double sup = std::numeric_limits<double>::max();
    
    #define EPSION 0
    
    struct Box
    {
        double lo[3] = { sup, sup, sup };
        double hi[3] = { inf, inf, inf };
    
        MyMesh::Point pt[3];
    };
    
    std::string GenerateUID(const Box* a)
    {
        std::stringstream ss;
        ss << a;
        return ss.str();
    }
    
    void UpdateBox(Box &box, const MyMesh::Point &pt)
    {
        for (int i = 0; i < 3; ++i)
        {
            if (pt[i] < box.lo[i])
                box.lo[i] = pt[i];
            if (pt[i] > box.hi[i])
                box.hi[i] = pt[i];
        }
    }
    
    struct BoxInterCallback
    {
        BoxInterCallback() = default;
        void operator()(const Box* a, const Box* b)
        {
            std::string uid = GenerateUID(a);
            if (mapInterBox.count(uid) == 0)
            {
                mapInterBox[uid] = a;
            }
            uid = GenerateUID(b);
            if (mapInterBox.count(uid) == 0)
            {
                mapInterBox[uid] = b;
            }
        }
    
        MapInterBox mapInterBox;
    };
    
    struct BoxPredicate 
    {
        static auto CompareFunc(int dim) 
        {
            return [=](const Box* a, const Box* b) -> bool {
                return IsLoLessLo(a, b, dim);
            };
        }
    
        static auto SpanningFunc(double lo, double hi, int dim)
        {
            // 如果[lo, hi)在box的范围内,那么返回true
            return [=](const Box* a) -> bool {
                return a->lo[dim] < lo - EPSION && a->hi[dim] > hi + EPSION;
            };
        }
    
        static auto LoLessFunc(double value, int dim)
        {
            return [=](const Box* a) -> bool {
                return a->lo[dim] < value - EPSION;
            };
        }
    
        static auto HiGreaterFunc(double value, int dim)
        {
            return [=](const Box* a) -> bool {
                return HiGreater(a->hi[dim], value);
            };
        }
    
        static bool HiGreater(double hi, double val)
        {
            return hi >= val + EPSION;
        }
    
        static bool IsLoLessLo(const Box* a, const Box *b, int dim)
        {
            return a->lo[dim] < b->lo[dim];
        }
    
        static bool IsLoLessHi(const Box* a, const Box *b, int dim)
        {
            return HiGreater(b->hi[dim], a->lo[dim]);
        }
    
        static bool IsIntersect(const Box* a, const Box* b, int dim)
        {
            return IsLoLessHi(a,b,dim) && IsLoLessHi(b,a,dim);
        }
    
        static bool ContainsLoPoint(const Box* a, const Box* b, int dim)
        {
            return IsLoLessLo(a,b,dim) && IsLoLessHi(b,a,dim);
        }
    };
    
    typedef std::function<void(const Box&, const Box&)> InterpCallback;
    
    template< class RandomAccessIter1, class RandomAccessIter2,
              class Callback>
    void OneWayScan(
        RandomAccessIter1 iBegin, RandomAccessIter1 iEnd,
        RandomAccessIter2 pBegin, RandomAccessIter2 pEnd, 
        int dim, Callback& callback)
    {
        /// 对P和对I(根据I的low endpoint)进行排序;
        std::sort(iBegin, iEnd, BoxPredicate::CompareFunc(0));
        std::sort(pBegin, pEnd, BoxPredicate::CompareFunc(0));
    
        /// 遍历interval
        for (auto i = iBegin; i != iEnd; ++i)
        {
            /// 对P进行遍历,直到找到p1不小于第一个intervals的low endpoints点
            for (; pBegin != pEnd&& BoxPredicate::IsLoLessLo((*pBegin), (*i), 0); ++pBegin) {}
    
            /// 继续对点进行遍历,直到找到一个点p2不小于第一个intervals的high endpoint点;
            /// 所有找到的点,属于第一个intervals;
            for (auto p1 = pBegin; p1 != pEnd && BoxPredicate::IsLoLessHi(*p1, *i, 0); ++p1)
            {
                /// 如果第d为大于0,那么还需要直接判断其他维度是否相交,
                /// 如果有某个维度不相交,那么i和p1就不相交
                bool bIntersect = true;
                for (int d=1; d<=dim; ++d)
                {
                    if (!BoxPredicate::IsIntersect(*p1, *i, d))
                    {
                        bIntersect = false;
                        break;
                    }
                }
                if (bIntersect)
                {
                    callback(*i, *p1);
                }
            }
        }
    }
    
    template<class RandomAccessIter1, class RandomAccessIter2,
             class Callback>
    void ModifiedTwoWayScan(
        RandomAccessIter1 iBegin, RandomAccessIter1 iEnd,
        RandomAccessIter2 pBegin, RandomAccessIter2 pEnd,
        int dim, Callback& callback)
    {
        std::sort(iBegin, iEnd, BoxPredicate::CompareFunc(0));
        std::sort(pBegin, pEnd, BoxPredicate::CompareFunc(0));
    
        while (iBegin != iEnd && pBegin != pEnd)
        {
            // 此时i作为interval,p为points
            if (BoxPredicate::IsLoLessLo(*iBegin, *pBegin, 0))
            {
                for (RandomAccessIter2 p = pBegin; p != pEnd && BoxPredicate::IsLoLessHi(*p, *iBegin, 0); ++p)
                {
                    bool bIntersect = true;
                    for (int d = 1; d <= dim; ++d)
                    {
                        if (!BoxPredicate::IsIntersect(*p, *iBegin, d))
                        {
                            bIntersect = false;
                            break;
                        }
                    }
    
                    if (bIntersect)
                    {
                        callback(*iBegin, *p);
                    }
                }
                ++iBegin;
            }
            else
            {
                // 将p作为interval,i作为point
                for (RandomAccessIter1 i = iBegin; i != iEnd && BoxPredicate::IsLoLessHi(*i, *pBegin, 0); ++i)
                {
                    bool bIntersect = true;
                    for (int d = 1; d <= dim; ++d)
                    {
                        if (!BoxPredicate::IsIntersect(*pBegin, *i, d))
                        {
                            bIntersect = false;
                            break;
                        }
                    }
    
                    if (bIntersect)
                    {
                        callback(*i, *pBegin);
                    }
                }
                ++pBegin;
            }
        }
    }
    
    template< class RandomAccessIter>
    RandomAccessIter SplitPoints(RandomAccessIter begin, RandomAccessIter end, int dim, double& mi)
    {
        std::ptrdiff_t delta = std::distance(begin, end);
        std::ptrdiff_t halfDelta = delta / 2;
        RandomAccessIter mid = begin + halfDelta;
        mi = (*mid)->lo[dim];
        return std::partition(begin, end, BoxPredicate::LoLessFunc(mi, dim));
    }
    
    template<class RandomAccessIter1, class RandomAccessIter2, class Callback>
    void SegmentTree( RandomAccessIter1 iBegin, RandomAccessIter1 iEnd,
                      RandomAccessIter2 pBegin, RandomAccessIter2 pEnd,
                      double lo, double hi, Callback& callback, std::ptrdiff_t cutoff, int dim)
    {
    
        if (pBegin == pEnd || iBegin == iEnd || lo >= hi)
        {
            return;
        }
    
        if (dim == 0)
        {
            //std::cout << "dim = 0. scanning ..." << std::endl;
            OneWayScan(iBegin, iEnd, pBegin, pEnd, dim, callback);
            return;
        }
    
        if (std::distance(pBegin, pEnd) < cutoff ||
            std::distance(iBegin, iEnd) < cutoff)
        {
            ModifiedTwoWayScan(iBegin, iEnd, pBegin, pEnd, dim, callback);
            return;
        }
    
        RandomAccessIter1 iSpanEnd = lo == inf || hi == sup ? iBegin : 
            std::partition(iBegin, iEnd, BoxPredicate::SpanningFunc(lo, hi, dim));
        
        if (iBegin != iSpanEnd)
        {
            SegmentTree(iBegin, iSpanEnd, pBegin, pEnd, inf, sup, callback, cutoff, dim-1);
            SegmentTree(pBegin, pEnd, iBegin, iSpanEnd, inf, sup, callback, cutoff, dim-1);
        }
    
        double mi(0);
        RandomAccessIter2 pMid = SplitPoints(pBegin, pEnd, dim, mi);
    
        if (pMid == pBegin || pMid == pEnd)
        {
            //std::cout << "unable to split points! performing modified two_way_scan ... " << std::endl;
            ModifiedTwoWayScan(iSpanEnd, iEnd, pBegin, pEnd, dim, callback);
            return;
        }
    
        RandomAccessIter1 iMid = std::partition(iSpanEnd, iEnd, BoxPredicate::LoLessFunc(mi, dim));
        //std::cout << "Processing ->left" << std::endl;
        SegmentTree(iSpanEnd, iMid, pBegin, pMid, lo, mi, callback, cutoff, dim);
        iMid = std::partition(iSpanEnd, iEnd, BoxPredicate::HiGreaterFunc(mi, dim));
        //std::cout << "Processing ->right" << std::endl;
        SegmentTree(iSpanEnd, iMid, pMid, pEnd, mi, hi, callback, cutoff, dim);
    }
    
    template<class RandomAccessIter1, class RandomAccessIter2, class Callback>
    void BoxIntersection(
        RandomAccessIter1 begin1, RandomAccessIter1 end1,
        RandomAccessIter2 begin2, RandomAccessIter2 end2,
        Callback& callback,
        std::ptrdiff_t cutoff = 10)
    {
        const int dim = 2; // start from 0
    
        SegmentTree(begin2, end2, begin1, end1, inf, sup, callback, cutoff, dim);
        SegmentTree(begin1, end1, begin2, end2, inf, sup, callback, cutoff, dim);
    }
    
    void ConvertMeshToBox(MyMesh& mesh, std::vector<Box>& vecBox, std::vector<Box*>& vecBoxPtr)
    {
        for (auto face : mesh.faces())
        {
            Box box;
            int i=0;
            for (auto vertice : face.vertices())
            {
                box.pt[i++] = mesh.point(vertice);
                UpdateBox(box, mesh.point(vertice));
            }
            vecBox.push_back(box);
        }
    
        for (int i = 0; i < vecBox.size(); ++i)
        {
            vecBoxPtr.push_back(&vecBox[i]);
        }
    }
    
    void WriteResult(const MapInterBox& result, const std::string& fileout)
    {
        std::ofstream of;
        of.open(fileout, std::ios::out);
        of << "STLFILE
    ";
        for (auto iter = result.begin(); iter != result.end(); ++iter)
        {
            auto pBox = iter->second;
            auto normal = (pBox->pt[0] - pBox->pt[1]).cross(pBox->pt[0] - pBox->pt[2]);
            normal.normalize();
            of << "facet normal " << normal[0] << " " << normal[1] << " " << normal[2] << "
    ";
            of << "outer loop
    ";
            for (int j=0; j<3; ++j)
            {
                auto pt = pBox->pt[j];
                of << "vertex " << pt[0] << " " << pt[1] << " " << pt[2] << "
    ";
            }
            of << "endloop
    ";
            of << "endfacet
    ";
        }
        of << "endsolidfilenamestl";
        of.close();
    }
    
    std::vector<Box> vecMeshBox1, vecMeshBox2;
    std::vector<Box*> vecMeshBoxPtr1, vecMeshBoxPtr2;
    
    void ReadMesh(MyMesh& mesh, std::string file)
    {
        if (!OpenMesh::IO::read_mesh(mesh, file))
        {
            std::cerr << "read " << file << " failed!" << std::endl;
        }
    }
    
    int test_intersection()
    {
        auto t1 = std::chrono::steady_clock::now();
        BoxInterCallback callback;
        BoxIntersection(vecMeshBoxPtr1.begin(), vecMeshBoxPtr1.end(), vecMeshBoxPtr2.begin(), vecMeshBoxPtr2.end(), callback);
        auto t2 = std::chrono::steady_clock::now();
        std::cout << "BoxIntersection cost: " << std::chrono::duration<double, std::milli>(t2-t1).count() << "ms" << std::endl;
    
        std::string fileout = "E:/stl/intersect_result_box.stl";
        WriteResult(callback.mapInterBox, fileout);
    
        return 0;
    }
    
    void test_one_way_scan()
    {
        BoxInterCallback callback;
        OneWayScan(vecMeshBoxPtr1.begin(), vecMeshBoxPtr1.end(), vecMeshBoxPtr2.begin(), vecMeshBoxPtr2.end(), 2, callback);
        OneWayScan(vecMeshBoxPtr1.begin(), vecMeshBoxPtr1.end(), vecMeshBoxPtr2.begin(), vecMeshBoxPtr2.end(), 2, callback);
    
        std::string fileout = "E:/stl/one_way_test_xyz_double.stl";
        WriteResult(callback.mapInterBox, fileout);
    }
    
    void test_modified_two_way_scan()
    {
        BoxInterCallback callback;
        ModifiedTwoWayScan(vecMeshBoxPtr1.begin(), vecMeshBoxPtr1.end(), vecMeshBoxPtr2.begin(), vecMeshBoxPtr2.end(), 2, callback);
    
        std::string fileout = "E:/stl/modified_two_way_scan.stl";
        WriteResult(callback.mapInterBox, fileout);
    }
    
    int main()
    {
        MyMesh mesh1, mesh2;
        ReadMesh(mesh1, "E:/stl/Reamers52.STL");
        ReadMesh(mesh2, "E:/stl/semi-sphere.stl");
    
        ConvertMeshToBox(mesh1, vecMeshBox1, vecMeshBoxPtr1);
        ConvertMeshToBox(mesh2, vecMeshBox2, vecMeshBoxPtr2);
    
        test_intersection();
        // test_one_way_scan();
        // test_modified_two_way_scan();
        return 0;
    }
    

    原始图如下:

    结果图如下:

  • 相关阅读:
    一款JS+CSS实现的无缝平滑图片滚动代码
    2个按钮控制的左右图片滚动特效代码
    JS+CSS控制左右切换鼠标可控的无缝图片滚动代码
    用CSS实现图片水印效果代码
    用鼠标拖动图片的JS代码
    一款实用的JavaScript图片幻灯片代码
    摘自搜狐女人频道的图片切换的JS代码
    JS防PS里的图片拖拉缩放效果代码
    鼠标移至图片后抖动的JS代码
    WINDOWS补丁的多线程下载方法和安装技巧
  • 原文地址:https://www.cnblogs.com/grass-and-moon/p/13361323.html
Copyright © 2011-2022 走看看