1. 程式人生 > >(五)建築物多邊形化簡系列——最小外接矩形的獲取

(五)建築物多邊形化簡系列——最小外接矩形的獲取

最小外接矩形問題是在給出一個多邊形(或一群點),求出面積最小且外接多邊形的矩形的問題。這個問題看起來並不難,但是具體實現並不簡單。除了呼叫現有的公開庫之外,這裡給出一種簡單且易理解的方法。

 

演算法的主要思想是:

(1)先實現多邊形的簡單外接矩形的演算法。簡單外接矩形是指邊平行於x軸或y軸的外接矩形。簡單外接矩形很有可能不是最小外接矩形,卻是非常容易求得的外接矩形,這為後面做鋪墊。

(2)實現平面上某一點繞固定點旋轉某一角度的演算法。數學基礎是,設平面上點(x1,y1)繞另一點(x0,y0)逆時針旋轉A角度後的點為(x2,y2),則有

x2 =(x1-x0)*cosA-(y1-y0)*sinA+x0

y2 =(x1-x0)*sinA+(y1-y0)*cosA+y0

順時針時,A改寫成-A即可。

(3)旋轉原始多邊形(迴圈,0-90°,間距設為1°),求旋轉每個度數後的多邊形的簡單外接矩形,記錄簡單外接矩形的面積、頂點座標以及此時旋轉的度數。

(4)比較在旋轉過程中多邊形求得的所有簡單外接矩形,得到面積最小的簡單外接矩形,獲取該簡單外接矩形的頂點座標和旋轉的角度。

(5)旋轉外接矩形。將上一步獲得面積最小的簡單外接矩形反方向(與第3步方向相反)旋轉相同的角度,即得最小外接矩形。

 

實現過程

(1)尋找多邊形的中心

多邊形的中心就是多邊形的重心,各個座標點之和求平均即可

CPoint* CGeoPolygon::FindCenter(vector<CPoint*> ptsArray)
{
	double tempX,tempY;
	double sumX = 0;
	double sumY = 0;
	int size = ptsArray.size();
	for(int i = 0;i<size;i++)
	{
		sumX = sumX + ptsArray[i]->x;
		sumY = sumY + ptsArray[i]->y;
	}
	tempX = sumX/size;
	tempY = sumY/size;
	CPoint* pt = new CPoint(tempX,tempY);
	return pt;
}

(2)旋轉多邊形,針對每個點實現繞中心點旋轉

旋轉的演算法見下,注意角度要轉換成弧度。

// 某一點pt繞center旋轉theta角度,zf,0706
CPoint* CGeoPolygon::rotate(CPoint* pt, CPoint* center, double theta)
{
	double x1 = pt->x;
	double y1 = pt->y;
	double x0 = center->x;
	double y0 = center->y;

	double Q = theta / 180 * 3.1415926;  //角度

	double x2,y2;
	x2 = (x1-x0)*cos(Q)-(y1-y0)*sin(Q)+x0;   //旋轉公式
	y2 = (x1-x0)*sin(Q)+(y1-y0)*cos(Q)+y0;

	CPoint* rotatePoint = new CPoint(x2,y2);
	//CPoint* rotatePoint = new CPoint((x1+10,y1+10));
	return rotatePoint;
}

(3)多邊形旋轉後求簡單外接矩形,簡單外接矩形演算法見下

void CGeoPolygon::FindRectangle(vector<CMyPoint*> pts)
{
	//AfxMessageBox("一般的外接矩形!");
	int size = pts.size();
	if(size == 0)
		AfxMessageBox("該環為空");
	else
	{
		double Xmax = 0;
		double Ymax = 0;
		double Xmin = 60000000;  //最小值不能初始為0,
		double Ymin = 1000000000;  //最小值不能初始為0,
		for(int i = 0;i<size;i++)
		{
			double tempx = pts[i]->Getx();
			double tempy = pts[i]->Gety();
			if(tempx>=Xmax) Xmax = tempx;  //最大x,
			if(tempy>=Ymax) Ymax = tempy;  //最大y,
			if(tempx<=Xmin) Xmin = tempx;  //最小x
			if(tempy<=Ymin) Ymin = tempy;  //最小y
		}
		CPoint *pt1 = new CPoint(Xmax,Ymax);  //左上
		CPoint *pt2 = new CPoint(Xmax,Ymin);
		CPoint *pt3 = new CPoint(Xmin,Ymin);
		CPoint *pt4 = new CPoint(Xmin,Ymax);

		rectangleArray.push_back(pt1);
		rectangleArray.push_back(pt2);
		rectangleArray.push_back(pt3);
		rectangleArray.push_back(pt4);
	}
}

這裡的rectangleArray是我自己工程的陣列,可以換成自己的。

上述三步為第一大步。


(4)儲存每個旋轉角度下多邊形的外接矩形,記錄外接矩形的頂點座標、面積和此時多邊形的旋轉角度

            vector<CPoint*> temp = FindRectangle(tempArray); //-----------2---------求旋轉後的外接矩形
            if(temp.size() == 0)
                AfxMessageBox("簡單外接矩形獲取失敗!");
            else
            {
                MBR_ZF *tempRect = new MBR_ZF();   //某個旋轉角度時的外接矩形指標
                for(int count = 0;count<temp.size();count++)    //將外接矩形頂點轉移到circles的變數中
                    tempRect->vertices.push_back(temp[count]);
                double deltaX,deltaY,tempS;                     //求每個外接矩形的面積
                deltaX = tempRect->vertices[0]->x - tempRect->vertices[2]->x;
                deltaY = tempRect->vertices[0]->y - tempRect->vertices[2]->y;
                tempS = deltaY * deltaX;
                tempRect->area = tempS;
                tempRect->ID = angle;
                circles[i]->mbr.push_back(tempRect);  //mbr是用於儲存旋轉過程中每個外接矩形的陣列,單組成元是每個物件的指標
            }

(5)比較每個外接矩形,確定每個環面積最小的外接矩形。

                //------3-----------比較每個外接矩形,確定每個環面積最小的外接矩形
		int finalID;
		double compare = 600000000;
		for(int num = 0;num<circles[i]->mbr.size();num++)
		{
			if(compare >= circles[i]->mbr[num]->area)
			{
				finalID = circles[i]->mbr[num]->ID;
				compare = circles[i]->mbr[num]->area;
			}
		}
		for(int num = 0;num<circles[i]->mbr.size();num++)
		{
			if(circles[i]->mbr[num]->ID == finalID)
			{
				circles[i]->finalMBR.area = circles[i]->mbr[num]->area;
				circles[i]->finalMBR.ID = circles[i]->mbr[num]->ID;
				for(int pointNum = 0; pointNum<circles[i]->mbr[num]->vertices.size();pointNum++)
					circles[i]->finalMBR.vertices.push_back(circles[i]->mbr[num]->vertices[pointNum]);
			}
		}

(6)將外接矩形旋轉回來。外接矩形朝相反的方向旋轉相同度數。

//----------4-------將外接矩形朝相反的方向旋轉相同度數
        int finalAngle = circles[i]->finalMBR.ID;
        for(int final = 0;final<circles[i]->finalMBR.vertices.size();final++)
            rectangleRotate.push_back(rotate(circles[i]->finalMBR.vertices[final],center,-finalAngle));  //!!!此處角度相反

ID就是旋轉的角度。

 

最終效果:

簡單外接矩形

最小外接矩形

 

總結:理解演算法的思路很重要,我的程式碼只是一個例子。這個方法不需要旋轉外接矩形,只需要旋轉多邊形求簡單外接矩形,思路上更容易理解。

PS:很感謝我的室友楊某給我的幫助,此方法受他啟發。