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)
{
}
測試效果: