計算幾何
基礎
向量叉乘
- A (x1,y1) B(x2,y2) A×B= x1y2-x2y1
向量點乘
- A (x1,y1) B(x2,y2) A·B=x1x2+y1y2 a*b=|a||b|cos0
凸包
Jarvis步進法: O(nH)
思路:
- 縱座標最小的那個點一定是凸包上的點,例如圖上的 P0。
- 從 P0 開始,按逆時針的方向,逐個找凸包上的點,每前進一步找到一個點,所以叫作步進法。
- 怎麼找下一個點呢?利用夾角。假設現在已經找到 {P0,P1,P2} 了,要找下一個點:剩下的點分別和 P2 組成向量,設這個向量與向量P1P2的夾角爲 β 。當 β 最小時就是所要求的下一個點了,此處爲 P3 。
注意:
- 找第二個點 P1 時,因爲已經找到的只有 P0 一個點,所以向量只能和水平線作夾角 α,當 α 最小時求得第二個點。
共線情況:如果直線 P2P3 上還有一個點 P4,即三個點共線,此時由向量P2P3 和向量P2P4 產生的兩個 β 是相同的。我們應該把 P3、P4 都當做凸包上的點,並且把距離 P2 最遠的那個點(即圖中的P4)作爲最後搜索到的點,繼續找它的下一個連接點。
Graham法: O(n㏒n)
時間複雜度:O(n㏒n)
思路:Graham掃描的思想和Jarris步進法類似,也是先找到凸包上的一個點,然後從那個點開始按逆時針方向逐個找凸包上的點,但它不是利用夾角。
步驟:
把所有點放在二維座標系中,則縱座標最小的點一定是凸包上的點,如圖中的P0。
把所有點的座標平移一下,使 P0 作爲原點,如上圖。
計算各個點相對於 P0 的幅角 α ,按從小到大的順序對各個點排序。當 α 相同時,距離 P0 比較近的排在前面。例如上圖得到的結果爲 P1,P2,P3,P4,P5,P6,P7,P8。我們由幾何知識可以知道,結果中第一個點 P1 和最後一個點 P8 一定是凸包上的點。
(以上是準備步驟,以下開始求凸包)
以上,我們已經知道了凸包上的第一個點 P0 和第二個點 P1,我們把它們放在棧裏面。現在從步驟3求得的那個結果裏,把 P1 後面的那個點拿出來做當前點,即 P2 。接下來開始找第三個點:
連接P0和棧頂的那個點,得到直線 L 。看當前點是在直線 L 的右邊還是左邊。如果在直線的右邊就執行步驟5;如果在直線上,或者在直線的左邊就執行步驟6。
如果在右邊,則棧頂的那個元素不是凸包上的點,把棧頂元素出棧。執行步驟4。
當前點是凸包上的點,把它壓入棧,執行步驟7。
檢查當前的點 P2 是不是步驟3那個結果的最後一個元素。是最後一個元素的話就結束。如果不是的話就把 P2 後面那個點做當前點,返回步驟4。
最後,棧中的元素就是凸包上的點了。
以下爲用Graham掃描法動態求解的過程:
應用
規則圖形
判斷點在線段上
點Q(x,y),線段P1P2 (x2-x1,y2-y1) 則向量共線 P1Q×P1P2 =0 即(x(y2-y1)-(x2-x1)y)= 0
並且 min(x1,x2) <=x<=max(x1,x2)&&min(y1,y2)<=y<=max(y1,y2)// 在線段內部
判斷兩線段相交
快速排斥實驗
- 兩個線段爲對角線組成的矩形R和矩形T 若R,T不想交-->不想交,若相交進行跨立實驗判斷
- 方法: 假設 P1 = (x1, y1), P2 = (x2, y2), Q1 = (x3, y3), Q2 = (x4, y4),設矩形 R 的 x 座標的最小邊界爲 RX1 = min(x1, x2),RX2=max(x1,x2) ,RY1=min(y1,y2)以此類推,將矩形表示爲 R = (RX1, RY1, RX2, RY2) 的形式,若兩矩形相交,則相交的部分構成了一個新的矩形 F,我們可以知道 F 的 FX1 = max(RX1, TX1), FY2 = max(RY1, TY1),
- FX2 = min(RX2, TX2), FY2 = min(RY2, TX2),得到 F 的各個值之後,只要判斷矩形 F 是否成立就知道 R 和 T 到底有沒有相交了,若 FX1 > FX2 或 FY1 > Fy1 則 F 無法構成,RT不相交,否則 RT相交
跨立實驗(互相跨立)
- Q1P1×Q1P2 * Q1Q2×Q1Q1P2 >=0 && Q1P2×P1P2 * P1P2×Q2P2 >=0
線段與圓相交
三種情況, 第一種, 兩個端點都在圓內,不相交,第二種,一個在圓外,一個圓內 一定相交;
第三種都在圓外,需要判斷;
double x,y,r;
vector<pair<double,double> > V;
typedef pair<double,double> PAIR;
PAIR yuan;
bool judge(PAIR P)// 判斷是否在圓內
{
if( (P.first-x)*(P.first-x) + (P.second-y)*(P.second-y) -r*r <=0)
return 1;
return 0;
}
bool Judis(PAIR P1,PAIR P2,double R) //線段與圓的關係
{
if(judge(P1)&&judge(P2))//都在圓內 不相交
return false;
if(!judge(P1)&&judge(P2)||judge(P1)&&!judge(P2))//一個圓內一個圓外 相交
return true;
double A,B,C,dist1,dist2,angle1,angle2;//Ax+By+C=0;//(y1-y2)x +(x2-x1)y +x1y2-y1x2=0
if(P1.first==P2.first)
A=1,B=0,C= -P1.first;
else if(P1.second==P2.second)
A=0,B=1,C= -P1.second;
else
{
A = P1.second-P2.second;
B = P2.first-P1.first;
C = P1.first*P2.second - P1.second*P2.first;
}
dist1 = A * yuan.first + B * yuan.second + C;
dist1 *= dist1;
dist2 = (A * A + B * B) * R * R;
if (dist1 > dist2) return false;//點到直線距離大於半徑r 不相交
angle1 = (yuan.first - P1.first) * (P2.first - P1.first) + (yuan.second - P1.second) * (P2.second - P1.second);
angle2 = (yuan.first - P2.first) * (P1.first - P2.first) + (yuan.second - P2.second) * (P1.second - P2.second);
if (angle1 > 0 && angle2 > 0) return true;//餘弦都爲正,則是銳角 相交
return false;//不相交
}
判斷線段和直線相交
判斷點在矩形內
判斷線段/折現/多邊形在矩形內
判斷矩形在矩形內
判斷圓在矩形內
不規則圖形
判斷點在多邊形
代碼:
https://paste.ubuntu.com/26455451/
#include <stdio.h>
#include <bits/stdc++.h>
#include <iostream>
using namespace std;
const double ESP=1e-6;
const double INFINV=1e10;
struct LineSegment{
struct Point{
double x,y;
};
Point Pt1,Pt2;
typedef vector<Point> Vector;
// 叉乘 |p0p1| x |p0p2|
double Multiply(Point p1,Point p2,Point p0)
{
return ( (p1.x-p0.x)*(p2.y-p0.y) - (p2.x-p0.x)*(p1.y-p0.y));
}
// 點是否在邊上
bool IsOnline (Point P,LineSegment line)
{
return( ( fabs( Multiply(line.Pt1,line.Pt2,P) ) )<ESP && ( (P.x-line.Pt1.x)*(P.x-line.Pt2.x) <=0 )&&
( (P.y-line.Pt1.y)*(P.y-line.Pt2.y) <=0 ) ) ;
}
bool Intersect(LineSegment L1,LineSegment L2)
{
return( (max(L1.Pt1.x, L1.Pt2.x) >= min(L2.Pt1.x, L2.Pt2.x)) &&
(max(L2.Pt1.x, L2.Pt2.x) >= min(L1.Pt1.x, L1.Pt2.x)) &&
(max(L1.Pt1.y, L1.Pt2.y) >= min(L2.Pt1.y, L2.Pt2.y)) &&
(max(L2.Pt1.y, L2.Pt2.y) >= min(L1.Pt1.y, L1.Pt2.y)) &&
(Multiply(L2.Pt1, L1.Pt2, L1.Pt1) * Multiply(L1.Pt2, L2.Pt2, L1.Pt1) >= 0) &&
(Multiply(L1.Pt1, L2.Pt2, L2.Pt1) * Multiply(L2.Pt2, L1.Pt2, L2.Pt1) >= 0) );
}
bool main_Judge_Polygon(const Vector& poly,Point P)
{
int n=poly.size();
int count=0;
LineSegment line;
line.Pt1=P;
line.Pt2.y=P.y;
line.Pt2.x= -INFINV;
for(int i=0;i<n;i++)
{
LineSegment side;
side.Pt1=poly[i];
side.Pt2=poly[ (i+1) % n];
if( IsOnline(P,side) )
{// 若點在邊上 直接返回
return true;
}
if( fabs(side.Pt1.y-side.Pt2.y) <ESP )
{//判斷side是否平行於X軸
continue;
}
if( IsOnline(side.Pt1, line) )
{
if( side.Pt1.y > side.Pt2.y )
count++;
}
else if( IsOnline(side.Pt2,line) )
{
if( side.Pt2.y >side.Pt2.y)
count++;
}
else if( Intersect (line ,side))
{
count++;
}
}
if(count%2==1)
return false;
return true;
}
}JK;
int main()
{
return 0;
}