点群の凸包を求める

セジビック著 「アルゴリズムC++」より



CClientDC *pDC;
pDC = new CClientDC(this);
CPoints ps;
ps.Mark(pDC,ps.Max);
int num=ps.GrahamScan(ps.Max);
ps.Show(pDC,num);
float CLine::Theta()
{
int dx,dy,ax,ay;
float t;
dx=p2.x-p1.x;
ax=abs(dx);
dy=p2.y-p1.y;
ay=abs(dy);
t= (ax+ay ==0) ? 0: (float)dy/(ax+ay);
if (dx<0) t=2-t;
else if(dy<0) t=4+t;
return t*90.0f;
}
CPointsクラスでは配列points[]で点群を記録します。点の最大数は29です。コンストラクタでランダムな点を生成します。ここでは、配列の
1..Max を利用します。0 番目の要素は 「センチネル」 として利用します。CPoints::CPoints()
{
//200*200 の範囲のランダムな点を1..Maxに生成
//points[0]はsentinel、points[MAX]も利用
Max=29;
for(int i=1;i<=Max;i++){
points[i].x=(int)((rand()/32000.0)*200)+20;
points[i].y=(int)((rand()/32000.0)*200)+20;
}
}
int CPoints::GrahamScan(int N)
{
//Find Base
int i,min,M;
//右下の点をp[1]にする
for(min=1,i=2;i<=N;i++)
if(points[i].y < points[min].y) min=i;
for(i=1;i<=N;i++)
if(points[i].y == points[min].y)
if(points[i].x > points[min].x) min =i;
//TRACE("base %d %d\n",min,points[min].y);
swap(1,min);//baseの点を1番にする
//走査開始
SortbyTheta(N);
points[0]=points[N];//Sentinel
for(M=3,i=4;i<=N;i++){
//M > M-1> iの点は時計回りでなくてはいけない
while(points[M].Ccw(points[M],points[M-1],points[i]) >= 0) M--;
M++;
swap(i,M);
}
return M;
}
void CPoints::SortbyTheta(int N)
{
int i,j,min;
float thj,thmin=360.0;
CLine L;
for(i=1;i<=N;i++){
min=i;
thmin=360.0;
L.p1=points[1];
for(j=i;j<=N;j++){
L.p2=points[j];
thj=L.Theta();
if(thj < thmin){
thmin=thj;
min=j;
}
}
swap(i,min);
//TRACE("sort:%d %f\n",min,thmin);
}
}