如何判断两条线段是否相交
本篇是在 【C++筆記】如何判斷2個線段相交 的基礎上加上自己的理解和實踐總結出的判斷兩線段是否相交的方法。
判斷兩條線段是否相交
先附上判斷函數
bool judge(int Ax1,int Ay1,int Ax2,int Ay2,int Bx1,int By1,int Bx2,int By2) {if(( max(Ax1,Ax2)>=min(Bx1,Bx2)&&min(Ax1,Ax2)<=max(Bx1,Bx2) )&& //判斷x軸投影( max(Ay1,Ay2)>=min(By1,By2)&&min(Ay1,Ay2)<=max(By1,By2) ) //判斷y軸投影){if(( (Bx1-Ax1)*(Ay2-Ay1)-(By1-Ay1)*(Ax2-Ax1) ) * //判斷B是否跨過A( (Bx2-Ax1)*(Ay2-Ay1)-(By2-Ay1)*(Ax2-Ax1) ) <=0 &&( (Ax1-Bx1)*(By2-By1)-(Ay1-By1)*(Bx2-Bx1) ) * //判斷A是否跨過B( (Ax2-Bx1)*(By2-By1)-(Ay2-By1)*(Bx2-Bx1) ) <=0){return 1;}elsereturn 0;}elsereturn 0; }判斷一共分為兩步,即code中的第一個if和第二個if
 第一步:判斷兩線段在x軸和y軸的投影是否有交,有任何一條軸沒有交點就不可能相交。(快速排斥實驗)
 第二步:判斷兩條直線是否相互跨過,用跨立來判斷,具體用到的知識是向量積。(跨立實驗)
第一步:快速排斥實驗
很好理解吧,如果兩線段在x,y的投影都不重合,是不可能會相交的。
 求解的方法也有很多種,這里我就介紹我理解的這個方法。
 拿x軸舉例,y軸可類比
投影要有重合(哪怕只是一個點也算),那么兩線段中任意一條線段的兩端點中x較大的那一個端點的x值一定要大于另一條線段的兩端點中x較小的那一個端點的x值,不然這兩線段一定是相離的,在x軸投影沒有重合。
前面是先A線段對B線段,后面是B線段對A線段
max(Ax1,Ax2)>=min(Bx1,Bx2)&&min(Ax1,Ax2)<=max(Bx1,Bx2)//判斷x max(Ay1,Ay2)>=min(By1,By2)&&min(Ay1,Ay2)<=max(By1,By2)//判斷y我還有一種非正式但很好理解的說法
 我們把投影到x軸上的投影線段看作兩個隊伍,這兩個隊伍要各派一個隊員進行比賽,投影線段上的每一個點就是一個隊員,點對應的值就是這個隊員的能力,兩投影線段有重合說明兩個隊伍都有獲勝或平手的可能,怎么樣才會出現兩個隊伍都有獲勝或平手可能呢?那就是兩個隊伍中任意一個隊伍能力最強的選手能力要>=另一個隊伍中能力最弱的選手,即max(A)>=min(B)&&max(B)>=min(A)。這樣,只要A派出最強的,B派出最弱的,A就會獲勝或平手;B派出最強的,A派出最弱的,B就會獲勝或平手。
第二步:跨立實驗
兩個坐標A(x1,y1),B(x2,y2),那么AxB的向量積就是x1y2-y1x2。
 我們假定一個向量積R,R=x1y2-y1x2。
 R<0 說明A在B的逆時針方向
 R=0 說明A與B共線,可能正向也可能反向
 R>0 說明A在B的順時針方向
我們證明兩線段的跨立就需要證明A跨立B且B跨立A
 如何證明跨立?
 我們以B跨立A舉例
 B跨立A的意思就是B線段與A所在的直線有交點。
 我們在A的兩端點中任意選一個端點,將它與B的兩個端點相連得到L1,L2
 
 若此時A線段向量在L1,L2的中間或L1,L2的邊上,就能說明B跨立A
 即L1,L2在A的不同的順逆時針方向,我們就可以分別求出兩個L的向量積,再將他們相乘,如果結果<=0,即向量積異號或有0。
完整代碼
#include <bits/stdc++.h> using namespace std; bool judge(int Ax1,int Ay1,int Ax2,int Ay2,int Bx1,int By1,int Bx2,int By2); int main() {int Ax1,Ay1,Ax2,Ay2;int Bx1,By1,Bx2,By2;while(cin >> Ax1 >> Ay1 >> Ax2 >> Ay2 >> Bx1 >> By1 >> Bx2 >> By2){if(judge(Ax1,Ay1,Ax2,Ay2,Bx1,By1,Bx2,By2))cout << "YES!" << endl ;elsecout << "NO" << endl ;}return 0; } bool judge(int Ax1,int Ay1,int Ax2,int Ay2,int Bx1,int By1,int Bx2,int By2) {if(( max(Ax1,Ax2)>=min(Bx1,Bx2)&&min(Ax1,Ax2)<=max(Bx1,Bx2) )&& //判斷x軸投影( max(Ay1,Ay2)>=min(By1,By2)&&min(Ay1,Ay2)<=max(By1,By2) ) //判斷y軸投影){if(( (Bx1-Ax1)*(Ay2-Ay1)-(By1-Ay1)*(Ax2-Ax1) ) * //判斷B是否跨過A( (Bx2-Ax1)*(Ay2-Ay1)-(By2-Ay1)*(Ax2-Ax1) ) <=0 &&( (Ax1-Bx1)*(By2-By1)-(Ay1-By1)*(Bx2-Bx1) ) * //判斷A是否跨過B( (Ax2-Bx1)*(By2-By1)-(Ay2-By1)*(Bx2-Bx1) ) <=0){return 1;}elsereturn 0;}elsereturn 0; }關于代碼優化
在 【C++筆記】如何判斷2個線段相交 中已經有談到,如果不要第一步,可能會出現兩條共線但不相交的線段判斷的錯誤,因為共線后向量積都為0,這種情況可以在第一步中被排除掉。
如果將第二個if的<=0改為<0呢,不就排除了共線不相交了嗎?但這樣的話我們把剛好相交(一點相交)和共線相交的情況也都排除了。
那我們允許向量積最多一個為0行不行呢,同樣,這樣我們把共線相交的情況和兩線段共用一個端點的情況也排除了
暫時還沒有想出更優的代碼
相關題目
P922
#include <bits/stdc++.h> using namespace std; bool judge(int Ax1,int Ay1,int Ax2,int Ay2,int Bx1,int By1,int Bx2,int By2); int main() {int Ax1,Ay1,Ax2,Ay2;int Bx1,By1,Bx2,By2;int n;while(cin >> n && n){while(n--){cin >> Ax1 >> Ay1 >> Ax2 >> Ay2 >> Bx1 >> By1 >> Bx2 >> By2;if(judge(Ax1,Ay1,Ax2,Ay2,Bx1,By1,Bx2,By2))cout << "YES" << endl ;elsecout << "no" << endl ;}}return 0; } bool judge(int Ax1,int Ay1,int Ax2,int Ay2,int Bx1,int By1,int Bx2,int By2) {if(( max(Ax1,Ax2)>=min(Bx1,Bx2)&&min(Ax1,Ax2)<=max(Bx1,Bx2) )&& //判斷x軸投影( max(Ay1,Ay2)>=min(By1,By2)&&min(Ay1,Ay2)<=max(By1,By2) ) //判斷y軸投影){if(( (Bx1-Ax1)*(Ay2-Ay1)-(By1-Ay1)*(Ax2-Ax1) ) * //判斷B是否跨過A( (Bx2-Ax1)*(Ay2-Ay1)-(By2-Ay1)*(Ax2-Ax1) ) <=0 &&( (Ax1-Bx1)*(By2-By1)-(Ay1-By1)*(Bx2-Bx1) ) * //判斷A是否跨過B( (Ax2-Bx1)*(By2-By1)-(Ay2-By1)*(Bx2-Bx1) ) <=0){return 1;}elsereturn 0;}elsereturn 0; }總結
以上是生活随笔為你收集整理的如何判断两条线段是否相交的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: 如何判断2个线段相交
- 下一篇: 学生消费记录管理系统(C语言 结构体,
