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)
{
}
测试效果: