求多邊形凸包(線性演算法)--陳氏凸包演算法--

小 樓 一 夜 聽 春 雨發表於2013-09-06

http://blog.sina.com.cn/s/blog_616e189f0100qc0u.html

 

陳氏凸包演算法演算法參考:Computing the convex hull of a simple polygon 作者:Chern-Lin Chen

陳氏演算法提供了一個線性效率求凸包的演算法,本文使用VS2008對演算法進行了測試,論文中有很多錯誤的地方,大家可以參考原始碼進行更正。話不多說,大家請看原始碼,和執行效果。

作者對原演算法的錯誤處進行了校正,可以作為大家學習參考使用,如果在公共場合使用本資源和演算法更正的內容請標明出處,並及時與作者取得聯絡。未經允許不得以任何形式在公共場合(包括論文)中使用或模仿本演算法。版權歸作者所有(中國石油大學(華東)QQ531961673)。

本文演算法更改和實現都是本人個人完成,如有轉載或使用,請標明出處,並與作者取得聯絡,謝謝。

 

#pragma once

#include <vector>

#include <deque>

#include <algorithm>

#include <cstddef>

#include <cmath>

#include<functional>

 

using std::vector;

using std::deque;

 

 

class covexHull

{

public:

     covexHull(void);

     ~covexHull(void);

 

     void compute();//凸多邊形計算的入口

     void draw(CClientDC & dc );//繪製凸多邊形

     void addPoint(const CPoint &point);//新增計算的節點

 

private:

     class PointAndAngle{//內部類,用於臨時過程的處理(用於計算簡單多邊形)

     public:

         CPoint point;

         double angle;

         bool operator < (const PointAndAngle & p1)const{//小於運算子過載,用於排序

              return angle < p1.angle;

         }

     };

 

     std::vector<CPoint>::iterator findLeftPoint();//找到最左邊的點

     int computeS(const CPoint & p1, const CPoint &p2, const CPoint &p3)const;//計算S

     void computeSimplePolygon();//計算簡單多邊形

     void computeCovexHull();//計算凸多邊形

private:

 

     vector<CPoint> m_points;//點集合(無序)

     deque<PointAndAngle> m_pointAndAngle;//簡單多邊形排序後點集合(有序)

     deque<CPoint> m_stack;//凸多邊形點集合(有序)

    

};

 

 

實現部分:

#include "StdAfx.h"

#include "covexHull.h"

 

covexHull::covexHull(void)

{

}

 

void covexHull::addPoint(const CPoint &point)

{

     m_points.push_back(point);

}

 

inline vector<CPoint>::iterator covexHull:: findLeftPoint()

{       

     //最左邊的點,就是x的值最小的點

     std::vector<CPoint>::iterator ret = m_points.begin();

     for(std::vector<CPoint>::iterator it = m_points.begin() ; it != m_points.end() ; ++it)

     {

         if(it->x < ret->x)

              ret = it;

     }

     return ret;

}

 

void covexHull::draw(CClientDC & dc)

{

     //先繪製所有的點

     for(vector<CPoint>::iterator it = m_points.begin() ; it != m_points.end() ; ++it)

     {

         dc.Ellipse(it->x-3, it->y-3, it->x+3, it->y+3);

     }

     //繪製簡單多邊形

     {

         deque<PointAndAngle>::iterator it =  m_pointAndAngle.begin();

         if(it != m_pointAndAngle.end())//防止列表為空

              dc.MoveTo(it->point.x,it->point.y);

         for(; it!= m_pointAndAngle.end() ; ++it)

         {

              dc.LineTo(it->point.x,it->point.y);

         }

         if(m_pointAndAngle.size() != 0)//防止列表為空

              dc.LineTo(m_pointAndAngle.begin()->point.x,m_pointAndAngle.begin()->point.y);

     }

     //繪製凸多邊形

     {

         CPen * oldPen;

         CPen * newPen = new CPen(PS_SOLID,1,RGB(255,0,0));//更改畫筆顏色

         oldPen = dc.SelectObject(newPen);

 

         deque<CPoint>::iterator it =  m_stack.begin();

         if(it != m_stack.end())

              dc.MoveTo(it->x,it->y);

         for(; it!= m_stack.end() ; ++it)

         {

              dc.LineTo(it->x,it->y);

         }

         if(m_stack.size() != 0)

              dc.LineTo(m_stack.begin()->x,m_stack.begin()->y);

 

         dc.SelectObject(&oldPen);

         delete newPen;

     }

 

}

 

void covexHull::compute()

{

     computeSimplePolygon();//先計算簡單多邊形

     computeCovexHull();//計算凸多邊形

}

 

void covexHull::computeSimplePolygon()

{

     m_pointAndAngle.clear();

     vector<CPoint>::iterator it = findLeftPoint();//先找到最左側的點

 

     CPoint point(it->x,it->y);//將這個點儲存下來

 

     m_points.erase(it);//將最左側的點從列表中刪除(因為這個點自身求角度無意義)

 

     PointAndAngle paa;

     for(vector<CPoint>::iterator i = m_points.begin() ; i != m_points.end() ; ++i)//計算所有點與最左側點的角度

     {

         paa.point = *i;

         if(i->x - point.x == 0)//要先判斷除數為的情況

         {

              if(i->y > point.y)

                   paa.angle = 90.0/360.0*atan(1.0)*4;//PI = atan(1.0)*4

              else

                   paa.angle = -90.0/360.0*atan(1.0)*4;

         }

         else

              paa.angle = atan(double(double(i->y - point.y)/double(i->x - point.x)));//計算角度

         m_pointAndAngle.push_back(paa);//放入簡單多變行容器

     }

 

     std::sort(m_pointAndAngle.begin(),m_pointAndAngle.end());//按照角度從小到大排序

     paa.point = point;

     m_pointAndAngle.push_front(paa);//最後將最左側的點放入集合

     m_points.push_back(point);//將最左側點放入點集合

    

}

 

int covexHull::computeS(const CPoint & p1, const CPoint &p2, const CPoint &p3)const

{

     return (p3.x - p1.x)*(-p2.y + p1.y) - (-p3.y + p1.y)*(p2.x - p1.x);//計算S,注意實際座標系與螢幕座標系之間的轉換

}

 

void covexHull::computeCovexHull()

{

     m_stack.clear();

 

     if(m_pointAndAngle.size() < 3)//當小於個點,就不用計算了

         return;

     m_stack.push_front(m_pointAndAngle.at(0).point);//先將前兩個點放入棧中

     m_stack.push_front(m_pointAndAngle.at(1).point);

 

     deque<PointAndAngle>::iterator it = m_pointAndAngle.begin() + 2;//迭代器先要移動兩個位置(因為那兩個位置已經放入棧中了)

 

     for(;it != m_pointAndAngle.end() ;)//迭代求解

     {

         if(computeS(m_stack.at(1),m_stack.at(0),it->point) > 0)//S大於,此時點在直線的右側

         {

              if(computeS(m_stack.back(),m_stack.front(),it->point) > 0)//S大於,將點壓入棧中,否則不壓入(不進棧,迭代器移動,相當於reject

              {

                   m_stack.push_front(it->point);

              }

              ++it;//迭代器移動

         }else//S小於說明點在直線左側,當前棧頂肯定不是凸點

         {

              m_stack.pop_front();//彈出棧頂

              if(m_stack.size() < 2)//棧內元素太少,將當前點直接填入棧中

                   m_stack.push_front(it->point);

              //注意這裡迭代器並沒有移動

         }

     }

    

}

 

 

covexHull::~covexHull(void)

{

}

測試效果:

求多邊形凸包(線性演算法)--陳氏凸包演算法--Computing <wbr>the <wbr>convex <wbr>hull <wbr>of <wbr>a <wbr>simple <wbr>polygon(原始碼)

完整原始碼:http://guanxinquan.download.csdn.net/上下載

相關文章