陈氏凸包算法—算法参考: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)

{

}

测试效果:



完整源码:​​javascript:void(0)​​上下载