問題:給定兩個矩形A和B,矩形A的左上角坐標為(Xa1,Ya1),右下角坐標為(Xa2,Ya2),矩形B的左上角坐標為(Xb1,Yb1),右下角坐標為(Xb2,Yb2)。
(1)設計一個算法,確定兩個矩形是否相交(即有重疊區域)
(2)如果兩個矩形相交,設計一個算法,求出相交的區域矩形
(1)對於這個問題,一般的思路就是判斷一個矩形的四個頂點是否在另一個矩形的區域內。這個思路最簡單,但是效率不高,並且存在錯誤,錯誤在哪里,下面分析一下。
如上圖,把矩形的相交(區域重疊)分成三種(可能也有其他划分),對於第三種情況,如圖中的(3),兩個矩形相交,但並不存在一個矩形的頂點在另一個矩形內部。所以那種思路存在一個錯誤,對於這種情況的相交則檢查不出。
仔細觀察上圖,想到另一種思路,那就是判斷兩個矩形的中心坐標的水平和垂直距離,只要這兩個值滿足某種條件就可以相交。
矩形A的寬 Wa = Xa2-Xa1 高 Ha = Ya2-Ya1
矩形B的寬 Wb = Xb2-Xb1 高 Hb = Yb2-Yb1
矩形A的中心坐標 (Xa3,Ya3) = ( (Xa2+Xa1)/2 ,(Ya2+Ya1)/2 )
矩形B的中心坐標 (Xb3,Yb3) = ( (Xb2+Xb1)/2 ,(Yb2+Yb1)/2 )
所以只要同時滿足下面兩個式子,就可以說明兩個矩形相交。
1) | Xb3-Xa3 | <= Wa/2 + Wb/2
2) | Yb3-Ya3 | <= Ha/2 + Hb/2
即:
| Xb2+Xb1-Xa2-Xa1 | <= Xa2-Xa1 + Xb2-Xb1
| Yb2+Yb1-Ya2-Ya1 | <=Y a2-Ya1 + Yb2-Yb1
(2) 對於這個問題,假設兩個矩形相交,設相交之后的矩形(?也可能是平行四邊形,但是也滿足這種情況)為C,且矩形C的左上角坐標為(Xc1,Yc1),右下角坐標為(Xc2,Yc2),經過觀察上圖,很顯然可以得到:
Xc1 = max(Xa1,Xb1)
Yc1 = max(Ya1,Yb1)
Xc2 = min(Xa2,Xb2)
Yc2 = min(Ya2,Yb2)
這樣就求出了矩形的相交區域。
另外,注意到在不假設矩形相交的前提下,定義(Xc1,Yc1),(Xc2,Yc2),且Xc1,Yc1,Xc2,Yc2的值由上面四個式子得出。這樣,可以依據Xc1,Yc1,Xc2,Yc2的值來判斷矩形相交。
Xc1,Yc1,Xc2,Yc2只要同時滿足下面兩個式子,就可以說明兩個矩形相交。
3) Xc1 <= Xc2
4) Yc1 <= Yc2
即:
max(Xa1,Xb1) <= min(Xa2,Xb2)
max(Ya1,Yb1) <= min(Ya2,Yb2)
繼續思考如下方法是否可行:
方法一:
如果兩個矩形相交,則必然存在線條交叉,而能交叉的線條只有橫線和豎線,兩根橫線或兩根豎線都不可能交叉。所以,這個問題就轉化成尋找是否存在交叉的橫線與豎線。(橫線重疊情況?)
另外,A線與B線交叉等價於B線與A線交叉,所以,只要寫一個函數就足夠用了,多調用幾次,反正計算機是專門做簡單而又煩瑣的工作的。
下面是這個函數:判斷一條橫線和一條豎線是否交叉。該函數的參數分別是:橫線左、橫線右,橫線Y,豎線上,豎線下,豎線X。
bool CrossLine(left, right, y, top, bottom, x) { //判斷一根橫線和一根豎線是否交叉 //橫線有三個參數:left, right和y //豎線有三個參數:top, bottom和x return (top < y) && (bottom > y)&& (left < x) && (right > x); }
下面是判斷兩個矩形是否相交的函數,把同一個函數多調用幾篇就OK了。
bool CrossRect(CRect &r1, CRect &r2) { //判斷兩個矩形是否相交,共8種,橫豎交叉 //從一個矩形中取出一條橫線,與另一矩形中的一條豎線判斷是否交叉 return CrossLine(r1.x1, r1.x2, r1.y1, r2.y1, r2.y2, r2.x1) || CrossLine(r1.x1, r1.x2, r1.y1, r2.y1, r2.y2, r2.x2) || CrossLine(r1.x1, r1.x2, r1.y2, r2.y1, r2.y2, r2.x1) || CrossLine(r1.x1, r1.x2, r1.y2, r2.y1, r2.y2, r2.x2) || CrossLine(r2.x1, r2.x2, r2.y1, r1.y1, r1.y2, r1.x1) || CrossLine(r2.x1, r2.x2, r2.y1, r1.y1, r1.y2, r1.x2) || CrossLine(r2.x1, r2.x2, r2.y2, r1.y1, r1.y2, r1.x1) || CrossLine(r2.x1, r2.x2, r2.y2, r1.y1, r1.y2, r1.x2); }
方法二:
matlab 具有強大的圖形處理功能,它剛好有那么一個函數可以實現這個功能,那就是 rectint 。
函數介紹:
RECTINT 矩形相交函數。
AREA = RECTINT(A,B) 返回的矩形相交部分的面積。
A和B是按位置向量指定的矩形
如果A和B各指定一個長方形,面積是一個標量。
A和B也可以是矩陣,其中每行是一個位置向量。那么返回的AREA是一個矩陣,是提供的所有矩形與 由A 或 B指定的矩形相交部分的面積。也就是說,如果A指定的矩形 是M × 4和B為N × 4,然后AERA是一個m × n矩陣,在面積(P,Q)是由指定的矩形面積來自 P 行的 A 和 Q 行的 B ,
注:位置向量是一個四元向量 [X,Y,寬度,高度], 這個X和Y指定矩形左下方的那個頂點的坐標寬度和高度意思是分別沿x軸 和Y軸的大小。
例:
>> axes('xlim',[0 10],'ylim',[0 10]) >> a = [1 1 2 2]; >> b = [3 4 5 6]; >> rectangle('position',a) >> rectangle('position',b) >> c = [1 1 3 3]; >> rectangle('position',c) >> rectint(a,c) ans = 4 >> rectint(b,c) ans = 0 >> rectint(a,b) ans = 0 >> d = [a;b]; >> rectint(d,c) % 矩陣的情況 ans = 4 0 >>
感覺很好用, 嘎嘎。。。有時間的話,將 open rectint 來看看里面的代碼機制,看它是怎么實現的。。。
方法三:
【解題思路】 假定矩形是用一對點表達的(minx,miny)(maxx, maxy) 那么兩個矩形rect1{(minx1,miny1)(maxx1, maxy1)}, rect2{(minx2,miny2)(maxx2, maxy2)} 相交的結果一定是個矩形,構成這個相交矩形rect{(minx,miny)(maxx, maxy)}的點對坐標是: minx = max(minx1, minx2) miny = max(miny1, miny2) maxx = min(maxx1, maxx2) maxy = min(maxy1, maxy2) 如果兩個矩形不相交,那么計算得到的點對坐標必然滿足 minx > maxx 或者 miny > maxy 判定是否相交,以及相交矩形是什么都可以用這個方法一體計算完成 【源代碼】 bool CardRectManager::IsIntersect(RECT* pRect1, RECT* pRect2) { int nMaxLeft = 0; int nMaxTop = 0; int nMinRight = 0; int nMinBottom = 0; // Get the max left. if (pRect1->left >= pRect2->left) { nMaxLeft = pRect1->left; } else { nMaxLeft = pRect2->left; } // Get the max top. if (pRect1->top >= pRect2->top) { nMaxTop = pRect1->top; } else { nMaxTop = pRect2->top; } // Get the min right. if (pRect1->right <= pRect2->right) { nMinRight = pRect1->right; } else { nMinRight = pRect2->right; } // Get the min bottom. if (pRect1->bottom <= pRect2->bottom) { nMinBottom = pRect1->bottom; } else { nMinBottom = pRect2->bottom; } // Judge whether intersects. if (nMaxLeft > nMinRight || nMaxTop > nMinBottom) { return false; } else { return true; } }
工作需要,我只使用了判斷兩矩形是否交叉功能。
-----------------------------------------------------------------------------------------------------------------------------------------
根據原文檔,對應寫了一個函數,用於判斷兩矩形是否交叉:
private bool CheckCross(Rectangle r1, Rectangle r2) { PointF c1 = new PointF(r1.Left + r1.Width / 2.0f, r1.Top + r1.Height / 2.0f); PointF c2 = new PointF(r2.Left + r2.Width / 2.0f, r2.Top + r2.Height / 2.0f); return (Math.Abs(c1.X - c2.X) <= r1.Width / 2.0 + r2.Width / 2.0 && Math.Abs(c2.Y - c1.Y) <= r1.Height / 2.0 + r2.Height / 2.0); }