計算幾何文檔(1)
DionysosLai2014-4-24
計算幾何主要研究解決幾何問題的算法。在現代工程和數學領域,計算幾何在圖形學、機器人技術、超大規模集成電路設計和統計等諸多領域有着十分重要的應用。同樣在遊戲領域,計算幾何對開發有特別的重要。在本文中,即接下來的文章,將對計算幾何常用的基本算法做一個全面的介紹,希望對您瞭解並應用計算幾何的知識解決問題起到幫助。同時,由於目前本人使用的是遊戲引擎引擎是Cocos2d-x,因此使用的一些類名是基於Cocos2d-x的,如果你也正好是使用這個引擎,可以直接移植代碼,但如果你不是使用此引擎,只要稍微更改一下代碼也可以試用。
1. 矢量的叉積
設矢量P = ( x1, y1 ),Q = ( x2, y2 ),則矢量叉積定義爲由(0,0)、p1、p2和p1+p2所組成的平行四邊形的帶符號的面積,即:P × Q = x1*y2 - x2*y1,其結果是一個標量。顯然有性質 P × Q = - ( Q × P ) 和 P × ( - Q ) = - ( P × Q )。一般在不加說明的情況下,本文下述算法中所有的點都看作矢量,兩點的加減法就是矢量相加減,而點的乘法則看作矢量叉積。
叉積方向:當使用右手座標系時,可以使用右手負責來表示叉積方向。伸開右手掌,使除項目以外的4隻手指指向P矢量的方向,再把4隻手指指向內彎曲指向Q方向,那麼拇指的方向便是叉積(P×Q)的方向。
根據,叉積方向定義,叉積的一個非常重要性質是可以通過它的符號判斷兩矢量相互之間的順逆時針關係:
若 P × Q > 0 , 則P在Q的順時針方向。
若 P × Q < 0 , 則P在Q的逆時針方向。
若 P × Q = 0 , 則P與Q共線,但可能同向也可能反向。
叉積代碼如下:
double vectorProduct(double x1, double y1, double x2, double y2) // 行列式
{
return (x1*y2-x2*y1); ///< 叉積
}
2. 折線段的拐向判斷
根據上文叉積性質,可以判斷兩條有公共頂點的線段方向。
原理如下:
折線段的拐向判斷方法可以直接由矢量叉積的性質推出。對於有公共端點的p0p1和p1p2,通過計算(p2 - p0) × (p1 - p0)的符號便可以確定折線段的拐向:
若(p2 - p0) × (p1 - p0)> 0,則p0p1在p1點拐向右側後得到p1p2。
若(p2 - p0) × (p1 - p0)< 0,則p0p1在p1點拐向左側後得到p1p2。
若(p2 - p0) × (p1 - p0) = 0,則p0、p1、p2三點共線。
代碼如下:///@brief 判斷折線的拐向,
///
///以線段P1爲參考,;
///@param[in] p1--線段1,p2--線段2
///@pre P1、P1必須有公共端點
///@return 1--左邊 0 -- 共線 -1---右邊
///@retval
///@post
///@author DionysosLai,[email protected]等
///@version 1.0
///@data 2014-04-10
int LineDebug::PolyLineDerection(const CCPoint p1, const CCPoint p0, const CCPoint p2)
{
float vectorProductResult = 0.0f;
vectorProductResult = (float)vectorProduct(p1.x-p0.x, p1.y-p0.y, p2.x-p0.x, p2.y-p0.y);
if (vectorProductResult < 1e-3 && vectorProductResult > -1e-3)
{
CCLOG("p1p0 and p2p0 is at the same dereciton.");
return 0;
}
else if (vectorProductResult >= 1e-3)
{
CCLOG("p2p0 is at p1p0's left dereciotn.");
return -1;
}
else if (vectorProductResult <= -1e-3)
{
CCLOG("p2p0 is at p1p0's right dereciotn.");
return 1;
}
}
3. 判斷點是否在矩形內
原理:只要判斷點的橫座標在矩形左右橫座標內,點的縱座標在矩形上下縱座標內即可。
代碼如下:///@brief 判斷點是否在矩形內(點在邊界也算)
///
///只要判斷該點的橫座標和縱座標是否夾在矩形的左右邊和上下邊之間即可。
///@param[in] p0 --- 要判斷的點 r0,r1矩形對角線的兩個端點
///@pre r0,r1必須可以形成一個矩形
///@return true---在矩形內 false ---不在矩形內;
///@retval
///@post
///@author DionysosLai,[email protected]等
///@version 1.0
///@data 2014-04-10
bool HelloWorld::pointIsInRect(const CCPoint p0, const CCPoint r0, const CCPoint r1)
{
/// 首先判斷r0 r1 是否可以組成一個矩形;----這個不用判斷,不然點在線上就判斷不出來
// if (r0.x == r1.x || r0.y == r1.y)
// {
// CCLOG("Rectangle cannot be maken up by the two points!");
// return false;
// }
/// 獲取矩形的4條邊界
float minX = r0.x > r1.x ? r1.x : r0.x;
float maxX = r0.x > r1.x ? r0.x : r1.x;
float minY = r0.y > r1.y ? r1.y : r0.y;
float maxY = r0.y > r1.y ? r0.y : r1.y;
/// 判斷點是否在矩形左右、上下邊界內
if ((p0.x >= minX && p0.x <= maxX) &&
(p0.y >= minY && p0.y <= maxY))
{
CCLOG("Point is in rectangle1");
return true;
}
else
{
CCLOG("Point is not in rectangle!");
return false;
}
}
4. 點在線段上
原理:先判斷點是否在線段形成的矩形上,這個保證點不會在線段的延長線上;再次判斷點與線段的2個端點是否共線。
代碼如下:
///@brief 判斷點是否在線段上
///
///
///@param[in] p0--點,p1,p2--線段兩個端點
///@pre
///@return true---在線段上, false---不在線段上
///@retval
///@post
///@author DionysosLai,[email protected]等
///@version 1.0
///@data 2014-04-10
bool LineDebug::pointIsAtLine(const CCPoint p0, const CCPoint p1, const CCPoint p2)
{
/// 先判斷是否點在以p1 p2爲對角線的矩形內
if (pointIsAtRect(p0, p1, p2))
{
/// 判斷p1p0, p2p0是否共線
if (0 == PolyLineDerection(p0, p1, p2))
{
CCLOG("Point in at the line.");
return true;
}
CCLOG("Point isn't at the line.");
return false;
}
CCLOG("Point isn't at the line.");
return false;
}
5. 判斷兩個矩形是否相交
原理:兩個矩形的中心點水平距離比兩個矩形長的和的一半小;中心點垂直距離比兩個矩形高的和的一半小。
代碼如下:
///@brief 判斷兩個矩形是否相交
///
///
///@param[in] aa,bb--矩形1一個對角線端點, cc,dd--矩形2一個對角線端點
///@pre
///@return true---相交, false---不相交
///@retval
///@post
///@author DionysosLai,[email protected]等
///@version 1.0
///@data 2014-04-10
bool LineMenu::isRectCollision(CCPoint aa, CCPoint bb, CCPoint cc, CCPoint dd)
{
CCPoint centre1 = ccp((aa.x + bb.x)/2.0f, (aa.y + bb.y)/2.0f); ///< 獲得中點值
CCPoint centre2 = ccp((cc.x + dd.x)/2.0f, (cc.y + dd.y)/2.0f);
float lengthX = abs(centre1.x - centre2.x); ///< 獲得兩個矩形中心的距離
float lengthY = abs(centre1.y - centre2.y);
float lengthRect1X = abs(aa.x - bb.x); ///< 獲得兩個矩形長和寬
float lengthRect1Y = abs(aa.y - bb.y);
float lengthRect2X = abs(cc.x - dd.x);
float lengthRect2Y = abs(cc.y - dd.y);
/// 這裏減去1是調整誤差用的。
return (lengthX < (lengthRect1X + lengthRect2X)/2.0f-1 && lengthY < (lengthRect1Y + lengthRect2Y)/2.0f-1) ? true : false;
}