convexHull實現

凸包算法

首先介紹一下整個算法的流程:

Initialize all points’ state to UNVISITED, and an empty convex hull H;
Mark three outmost unvisited points v0,v1,v2 with state VISITED, and put them in H with CCW;
while(#UNVISITED points > 0)
    Pick one unvisited point P, and mark it with state VISITED;
    for each directed edge AB in H do
        if Orient(A,B,P) is CW then
            remove AB from H;
        end if
    end for
    connect P to the boundary of H, and keep H with CCW;
end while;

其中,orientation用於判斷點P是否與有向邊AB構成非CCW的三角形
Orient(i,j,k)=det(ji,ki)=det(xjxiyjyixkxiykyi)

The triangle [i,j,k] has positive orientation(CCW) if Orient(i,j,k) > 0,
negative orientation (CW) if Orient(i,j,k) < 0, and zero orientation
(vertices i,j,k are collinear) if Orient(i,j,k) = 0.

定義數據結構myEdge存儲有向邊的souce跟target兩個vertex

typedef struct myEdge 
    {
        CVertex* _vertex;
        CVertex* _source;
        myEdge* next;
        myEdge* prev;


        myEdge() 
        {
            _vertex = NULL;
            _source = NULL;
            next = NULL;
            prev = NULL;
        }

        CVertex*& vertex() { return _vertex; }
        CVertex*& source() { return _source; }
        myEdge*& he_next() { return next; }
        myEdge*& he_prev() { return prev; }

    } myEdge;

並聲明全局變量std::vector<myEdge*> lboundary; 存儲凸包的輪廓

orientation函數

double orientation(CPoint pi, CPoint pj, /*CPoint *pk,*/ CPoint p)   //i->j->p
    {
        double m[2][2] = { { pj[0] - pi[0], /*pk[0] - pi[0],*/ p[0] - pi[0] },
                           { pj[1] - pi[1], /*pk[0] - pi[0],*/ p[1] - pi[1] } };
                           /*{ pj[0] - pi[0], pk[0] - pi[0], p[0] - pi[0] }}*/
        cv::Mat M = cv::Mat(2, 2, CV_64F, m);
        return cv::determinant(M);
    }

    double orientation(myEdge* he, CVertex* v) 
    {
        double result = orientation(he->source()->point(),
                           he->vertex()->point(),
                           v->point());
        // std::cout << "from " << he->source()->point() << " to " << he->vertex()->point() << " with " << v->point() << "\t:" << result << "\n";
        return result;
    }

connectBoundary函數

void connectBoundary(CVertex* v) { 
        myEdge *next = NULL, *pre = NULL;
        for (std::vector<myEdge*>::iterator it = lboundary.begin(); it!= lboundary.end(); ++it) {
            if ((*it)->he_next() == NULL) {
                pre = *it;
            }
            if ((*it)->he_prev() == NULL) {
                next = *it;
            }
        }             //尋找舊邊界的起始(next)與終止位置(pre)
        assert ( pre != NULL && next != NULL);

        myEdge *_nhe1 = new myEdge(),
               *_nhe2 = new myEdge();

        _nhe1->vertex() = v;
        _nhe1->source() = pre->vertex();
        _nhe2->vertex() = next->source();
        _nhe2->source() = v;
        //linking to each other
        pre->he_next() = _nhe1;
        _nhe1->he_prev() = pre;
        _nhe1->he_next() = _nhe2;
        _nhe2->he_prev() = _nhe1;
        _nhe2->he_next() = next;
        next->he_prev() = _nhe2;

        lboundary.push_back(_nhe1);
        lboundary.push_back(_nhe2);
    }

makeConvexHull函數

void makeConvexHull() {
        //---------------初始化一個三角形------------------
        CFace *face = mesh.idFace(1);
        myEdge    *h0 = new myEdge(),
                  *h1 = new myEdge(),
                  *h2 = new myEdge();
        h0->vertex() = face->halfedge()->vertex();
        h1->vertex() = face->halfedge()->he_next()->vertex();
        h2->vertex() = face->halfedge()->he_prev()->vertex();
        lboundary.push_back(h0);
        lboundary.push_back(h1);
        lboundary.push_back(h2);
        //linking to each other
        for( size_t i = 0; i < lboundary.size(); i ++ )
        {
            lboundary[i]->he_next() = lboundary[(i+1)%3];
            lboundary[i]->he_prev() = lboundary[(i+2)%3];
            lboundary[i]->source() = lboundary[i]->he_prev()->vertex();
        }
        //-------------------開始算法----------------------

        for (CMyMesh::MeshVertexIterator viter(&mesh); !viter.end(); ++viter) {
            //picked one point

            CVertex* _v_ = *viter;
            if (_v_ != h0->vertex()
                && _v_ != h1->vertex()
                && _v_ != h2->vertex()) {
                //for each edge in boundary
                bool flag = false;
                for (std::vector<myEdge*>::iterator it = lboundary.begin(); it!= lboundary.end();) {
                    myEdge *_he_ = *it;
                    double result = orientation(_he_, _v_);
                    if (result < 0) {       
                        flag = true;
                        myEdge* &he = _he_;
                        it = lboundary.erase(it);
                        if (he->he_prev())
                            he->he_prev()->he_next() = NULL;
                        if (he->he_next())
                            he->he_next()->he_prev() = NULL;  //so as to find boundary
                        delete he;
                    } else {
                        ++it;
                    }
                }
                if (flag) {     //若處於舊邊界外邊,則成爲新邊界的一員
                    connectBoundary(_v_);
                }
            }
        }
    }

使用方法

只需隨機生成點列,初始化一個face就好了

    Vertex 1  0.692562 0.586054 0
    Vertex 2  0.933505 0.911277 0
    Vertex 3  0.638719 0.700207 0
    Vertex 4  0.147679 0.523002 0
    Vertex 5  0.18174 0.274551 0
    Vertex 6  0.882442 0.518354 0
    Vertex 7  0.270854 0.684367 0
    Vertex 8  0.143773 0.722004 0
    Vertex 9  0.9122 0.872061 0
    Vertex 10  0.442775 0.381621 0
    Face 1  1 2 3

調用方法如下:

    convexHull::my_read_m(argv[1]);
    convexHull::printFaceInfo(mesh.idFace(1)); 

    convexHull::makeConvexHull();
    convexHull::printBoundaryInfo();

將結果可視化

這裏採用openGL實現:

畫出凸包
    glBegin(GL_LINE_LOOP);
    convexHull::myEdge* edge = convexHull::lboundary[0], *t = edge->he_next();
    glVertex3d((edge->vertex()->point())[0], (edge->vertex()->point())[1], (edge->vertex()->point())[2]);
    while (t->vertex() != edge->source()) {
        glVertex3d((t->vertex()->point())[0], (t->vertex()->point())[1], (t->vertex()->point())[2]);
        t = t->he_next();
    }
    glVertex3d((t->vertex()->point())[0], (t->vertex()->point())[1], (t->vertex()->point())[2]);
    glEnd();
畫出隨機點

    glColor3f(0.0f, 0.0f, 0.0f);
    glPointSize(2.0f);
    glBegin(GL_POINTS);
    for(CMyMesh::MeshVertexIterator viter(&mesh); !viter.end(); ++viter) {
        CPoint p = (*viter)->point();
        glVertex3d(p[0], p[1], p[2]);
    }
    glEnd();

以下是100個隨機點時的結果:
這裏寫圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章