求多边形凸包(线性算法)--陈氏凸包算法--

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/上下载

posted @ 2013-09-06 16:58  小 楼 一 夜 听 春 雨  阅读(1225)  评论(0编辑  收藏  举报