点群の凸包を求める
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); } }