二維幾何-點和直線
這篇文章講的是二維幾何中點和直線的相關問題。
直線的引數表示。直線可以用直線上的一點P0和方向向量v表示(雖然這個向量的大小沒什麼用處)。直線上所有點P滿足P=P0+tv。其中t稱為引數。如果已知直線上的兩個不同點A和B,則方向向量為B-A,所以引數方程為A+(B-A)t。
引數方程最方便的地方在於直線、射線和線段的方程形式是一樣的。區別僅僅在於引數。直線的t沒有範圍限制。射線的t>0,線段的t在0~1之間,這樣,很多對於直線適用的公式可以很方便的用在射線和線段上。
直線交點:
設直線分別為P+tv和Q+tw,設向量u=P-Q,交點在第一條直線的引數為t1,第二條直線上的引數為t2,則x和y座標可以列出一個方程解得:
t1=cross(w,u) / cross(v,w)
t2 = cross(v,u) / cross(v,w)
呼叫前請確保兩條直線P+tv和Q+tw有唯一交點。當且僅當Cross(v,w)非0
如果P,v,Q,w的各個分量均為有理數,則交點座標也是有理數。在精度要求極高的情況下,可以考慮自定義分數類。
Point GetLineIntersection(Point P, Vector v, Point Q, Vector w) { Vector u; double t; u = P - Q; t = Cross(w, u) / Cross(v, w); return P + v * t; }
點到直線的距離:
點到直線的距離是一個常用的函式。可以用叉積算出。即用平行四邊形的面積除以底。
double DistanceToLine(Point P, Point A, Point B)
{
Vector v1, v2;
v1 = B - A;
v2 = P - A;
return fabs(Cross(v1, v2)) / Length(v1); //如果不取絕對值,得到的是有向距離
}
點到線段的距離。
設投影點為Q,如果Q線上段AB上,則所求距離就是P點到直線AB的距離;如果Q在射線BA上,則所求距離為PA距離;否則為PB距離,判斷Q的位置可以用點積進行(參見上篇 :兩個向量的為位置關係)。
double DistanceToSegment(Point P, Point A, Point B)
{
Vector v1, v2, v3;
v1 = B - A;
v2 = P - A;
v3 = P - B;
if(A == B) //A點等於B點
return Length(v2);
if(dcmp(Dot(v1, v2)) < 0) //Q在射線BA上
return Length(v2);
else if(dcmp(Dot(v1, v3)) > 0) //Q在射線AB上
return Length(v3);
else
return fabs(Cross(v1, v2)) / Length(v1);
}
點在直線上的投影。
我們把直線AB寫成引數式A+tv(v為向量AB),並且設Q的引數為t0,那麼Q=A+t0v,根據PQ垂直與AB,兩個向量的點積應該為0。
因此Dot(v,P-(A+t0v))=0.根據分配率,有Dot(v,P-A)-t0*Dot(v,v)=0,這樣就可以解出t0,從而得到Q點。
Point GetLineProjection(Point P, Point A, Point B)
{
Vector v;
v = B - A;
return A + v * (Dot(v, P-A) / Dot(v, v));
}
.線段相交判定。給定兩條線段,判斷是否相交。我們定義規範相交為兩線段恰好有一個公共點,且不在任何一條線段的端點。線段規範相交的充要條件是:每條線段的兩個端點都在另一條線段的兩側(這裡的兩側是指叉積的符號不同)
bool SegmentProperIntersection(Point a1, Point a2, Point b1, Point b2)
{
double c1, c2, c3, c4;
c1 = Cross(a2-a1, b1-a1);
c2 = Cross(a2-a1, b2-a1);
c3 = Cross(b2-b1, a1-b1);
c4 = Cross(b2-b1, a2-b1);
return (dcmp(c1) * dcmp(c2) < 0) && (dcmp(c3) * dcmp(c4) < 0);
}
如果允許在端點處相交,情況就比較複雜了。如果c1和c2都是0,表示兩線段共線,這時可能會有部分重疊的情況。如果c1和c2不都是0,則只有一種相交方法,即某個端點在另外一條線段上,為了判斷上述情況是否發生,還需要判斷一下點是否在一條線段(不包含端點)。
bool OnSegment(Point P, Point a1, Point a2)
{
return (dcmp(Cross(a1-P, a2-P)) == 0) && (dcmp(Dot(a1-P, a2-P)) < 0);
}
判斷一個點是否在一條線段上.首先判斷是否是端點,不是的話判斷是否線上段上。
bool isPointOnSegment(Point p, Point a1, Point a2)
{
return (p==a1) || (p== a2) || OnSegment(p, a1, a2);
}