射线法学习记录
射線法的優(yōu)化
- 1.射線法簡(jiǎn)述
- 具體處理
- python代碼
1.射線法簡(jiǎn)述
因?yàn)楣ぷ餍枰?#xff0c;所以需要判斷某個(gè)坐標(biāo)的點(diǎn)是不是在某個(gè)區(qū)域范圍(單個(gè)閉合多邊形)內(nèi)。經(jīng)過(guò)在網(wǎng)上搜索資料,發(fā)現(xiàn)射線法能解決我的問(wèn)題,并且十分巧妙,記錄一下。
據(jù)說(shuō)這個(gè)辦法也叫==奇偶規(guī)則法==,如果比較想一探究竟的話可以去了解一下,具體證明什么的我也無(wú)能為力,good luck。
原理:
1.如果一個(gè)點(diǎn)在一個(gè)多邊形內(nèi)部,那么以它為起始點(diǎn),發(fā)射一條射線(任意方向),那么它至少會(huì)和一條邊相交,也就是有一個(gè)交點(diǎn)。如圖所示
2.如果點(diǎn)在多邊形內(nèi)部出發(fā)射線不止和一條邊相交,在不經(jīng)過(guò)端點(diǎn)和整條邊的情況下,一定和邊有奇數(shù)個(gè)交點(diǎn)(我也不知道怎么證明,但是畫(huà)了很多圖,確實(shí)是這么神奇。如果有大佬能證明再好不過(guò)了),看圖,有3個(gè)交點(diǎn)
3.如果經(jīng)過(guò)端點(diǎn)和邊需要單獨(dú)討論。
綜上,如果一個(gè)點(diǎn)在閉合多邊形內(nèi)部,那么它和多邊形的交點(diǎn)是奇數(shù)個(gè)。
具體處理
1.單個(gè)閉合多邊形需要端點(diǎn)都按順時(shí)針或者逆時(shí)針順序構(gòu)建。這是構(gòu)建多邊形的基礎(chǔ),如果這一步出錯(cuò)了,后面的都無(wú)法進(jìn)行
2.由于兩個(gè)點(diǎn)A(x1,y1)、B(x2,y2)形成的坐標(biāo)系方程是y=(y2(x-x1)+y1(x2-x))/(x2-x1),除數(shù)是x2-x1,如果A、B橫坐標(biāo)相同,則除數(shù)是0,為了避免這個(gè)問(wèn)題,我們?nèi)∧繕?biāo)點(diǎn)沿y軸正半軸平行線做射線,就像上面畫(huà)的那樣,也為了偷個(gè)懶,代入目標(biāo)點(diǎn)的橫坐標(biāo),得到連線和射線的交點(diǎn)縱坐標(biāo)y,如果交點(diǎn)縱坐標(biāo)大于目標(biāo)點(diǎn)縱坐標(biāo),則必定相交,交點(diǎn)數(shù)+1,如果剛好相等,就在邊上,直接返回在多邊形內(nèi)部(方程可以自己求,萬(wàn)一我錯(cuò)了我就是罪人,畢竟好久不讀書(shū)了,勿噴)
3.如果射線經(jīng)過(guò)了一個(gè)端點(diǎn),無(wú)論計(jì)算兩次還是1次都會(huì)出問(wèn)題,如圖
統(tǒng)一按2個(gè)或者1個(gè)算都是錯(cuò)的,所以處理方式是穿過(guò)兩個(gè)點(diǎn)中的較大(或者較小)x軸坐標(biāo)記一次交點(diǎn),仍然是奇數(shù)次為圖形內(nèi)部,不信可以試試(反正我不會(huì)證明)(如果是從x軸發(fā)射射線,那就是取較大或者較小的y坐標(biāo)記錄交點(diǎn))
4.經(jīng)過(guò)一條邊,也就是平行于y軸,這條邊不計(jì)算交點(diǎn)
5.剛好在邊上/就是端點(diǎn),很簡(jiǎn)單,就不說(shuō)了
6.對(duì)于max(y1,y2)<y的線段,不進(jìn)行判斷,因?yàn)槭窍蛏先ト∩渚€,所以絕對(duì)不會(huì)有任何交點(diǎn),加快速度
7.同理,對(duì)于max(x1,x2)<x或者min(x1,x2)>x的線段,不進(jìn)行判斷,因?yàn)榫€段是平行于y軸的,所以絕對(duì)不會(huì)與線段有任何交點(diǎn),加快速度
python代碼
# 測(cè)試邊界樣例0.0,0.0;1.0,0.0;1.0,1.0;0.0,1.0 # 用;分割坐標(biāo)點(diǎn),用,分割橫縱坐標(biāo) class Point:def __init__(self, lng, lat):try:self.lng = float(lng)self.lat = float(lat)except Exception as e:self.lng = 0self.lat = 0class Bound:def __init__(self, bounds, **kwargs):self.attribute = kwargspoints = bounds.split(';')points_list = list()for point in points:lng = float(point.split(',')[0])lat = float(point.split(',')[1])points_list.append(Point(lng, lat))self.bounds = points_listdef inrange(self, point: Point):if isinstance(point, Point):passelse:# print("請(qǐng)檢查傳入的點(diǎn)類型")raise TypeError("傳入類型應(yīng)該是Point類型而不是" + str(type(point)) + "類型")pos = 0inrange = Falsewhile pos < len(self.bounds):beg_point = self.bounds[pos]end_point = self.bounds[(pos + 1) % len(self.bounds)]# print(self.attribute['ywzm'],beg_point.lng,beg_point.lat,end_point.lng,end_point.lat)# 固定射線往y軸正半軸射出,則它只會(huì)與x軸坐標(biāo)區(qū)間包含該點(diǎn)的射線相交# 1.與端點(diǎn)連線線段相交(可能在連線上)if min(beg_point.lng, end_point.lng) < point.lng < max(beg_point.lng, end_point.lng): # 只要線段的x軸坐標(biāo)包含點(diǎn)坐標(biāo)就有可能相交,否則不可能,過(guò)濾掉if point.lat < max(beg_point.lat, end_point.lat): # 只要該點(diǎn)的y軸坐標(biāo)小于射線兩端點(diǎn)的任意一個(gè)y軸坐標(biāo),則有可能相交# 構(gòu)建兩點(diǎn)的方程,判定目標(biāo)點(diǎn)的射線與線段的交點(diǎn)y軸坐標(biāo)是否在點(diǎn)上方,是則相交,否則不交if (end_point.lat * (point.lng - beg_point.lng) + beg_point.lat * (end_point.lng - point.lng)) / (end_point.lng - beg_point.lng) > point.lat:# print(beg_point.lng, beg_point.lat, end_point.lng, end_point.lat)inrange = not inrange# 在邊上就不執(zhí)行了,直接判定在內(nèi)部elif (end_point.lat * (point.lng - beg_point.lng) + beg_point.lat * (end_point.lng - point.lng)) / (end_point.lng - beg_point.lng) == point.lat:# print(beg_point.lng, beg_point.lat, end_point.lng, end_point.lat)inrange = Truebreak# print(inrange)# 2. 剛好就是端點(diǎn)或者經(jīng)過(guò)端點(diǎn),則以穿過(guò)起始點(diǎn)坐標(biāo)為相交,終止點(diǎn)不交,起始點(diǎn)選擇為x軸坐標(biāo)小的點(diǎn)elif point.lng == beg_point.lng or point.lng == end_point.lng:# print(beg_point.lng, beg_point.lat, end_point.lng, end_point.lat)# 3.剛好線段與y軸平行if beg_point.lng == end_point.lng: # 與y軸平行不算,但是需要考慮是不是在點(diǎn)是不是在線段上或者端點(diǎn)if min(beg_point.lat, end_point.lat) <= point.lat <= max(beg_point.lat, end_point.lat): # 點(diǎn)在線段上跳出循環(huán)inrange = Truebreakelse:if point.lng == min(beg_point.lng, end_point.lng): # 穿過(guò)起始點(diǎn)記錄一次inrange = not inrange# print(inrange)pos += 1# print(inrange)return inrange第一次寫,有問(wèn)題請(qǐng)指正,有時(shí)間一起學(xué)習(xí)
總結(jié)
- 上一篇: unity发射斜射线_Unity发射射线
- 下一篇: Unity 2D射线基本使用和画线