喜加一:《默途》為奧運健兒慶功,將開啟38%限時折扣
點、線段、多邊形
計算幾何
點積
向量的內積(點乘/數量積)
\[a=[a_1,a_2,...a_n] \quad b=[b_1,b_2,....,b_n]\\ a\cdot b=a_1b_1+a_2b_2+....a_nb_n \]注意:點乘的結果是一個標量
a·b = |a||b|cos∠(a, b)
若 a
,b
正交,則 a.b=0
內積的幾何意義
- 表徵或計算兩個向量之間的夾角
- b向量在a向量方向上的投影
def inner_prod(point1, point2): # 計算 向量內積 # point1 (x,y) # point2 (x,y) # x1*x2+y1*y2+... return point1[0] * point2[0] + point1[1] * point2[1]
叉積
向量的外積,又稱叉積,是向量代數(解析幾何)中的一個概念
向量叉乘(行列式計算):向量a(x1,y1)
,向量b(x2,y2)
axb=A||B|Sin(θ)
a^b
=x1y2-x2y1
若結果大於0,表示向量b在向量a的逆時針方向;若等於0,表示向量a與向量b平行。**
(順逆時針是指兩向量平移至起點相連,從某個方向旋轉到另一個向量小於180度)
計算向量叉積是與直線和線段相關演算法的核心部分
- 向量的叉積的模表示這兩個向量圍成的平行四邊形的面積
- a×b(×為向量叉乘),若結果小於0,表示向量b在向量a的順時針方向
- axb 結果大於0,表示向量b在向量a的逆時針方向
- 若結果等於 0 則說明 a b 共線
double PointOffLine( Point pt, Point start, Point end )
{
return ((pt.x - end.x)*(start.y - end.y)-(start.x - end.x)*(pt.y - end.y))*1.;
}
def cross(p0, p1, p2): """ 向量a×向量b (×為向量叉乘) 向量 a (p1x-p0x,p1y-p0y) p1->p0 向量 b (p2x-p0x,p2y-p0y) p2->p0 :param p1: 起始點 (x,y) :param p2: 終點1 (x,y) :param p3: 終點2 (x,y) :return: """ # 計算叉積 # 若結果小於0,表示向量b在向量a的順時針方向; # 若結果大於0,表示向量b在向量a的逆時針方向; # 若等於0,表示向量a與向量b 方向重合 x1 = p1[0] - p0[0] y1 = p1[1] - p0[1] x2 = p2[0] - p0[0] y2 = p2[1] - p0[1] return x1 * y2 - x2 * y1
點與點距離
計算平方根
點到直線的距離
- 方法一、通過點到直線的公式
過點P向線段AB上畫垂線,判斷垂足有沒有落線上段上。如果落線上段上,ok,距離就是垂線段的長度;如果沒有,則距離轉化為點到線段兩端點的距離。
原理
求解直線方程
\[\frac{y_2-y_1}{x_2-x_1}=\frac{y_2-y_1}{x-x_1} \]\[A=y_2-y_1\\ B=x_1-x_2\\ C=x_1*(y_1-y_2) \]點到直線的距離公式
\[\frac{|A*x0+B*y0+C|}{\sqrt{A^2+B^2}} \]def get_distance_from_point_to_line(point, line_point1, line_point2):
"""
:param point: 點座標 (x,y)
:param line_point1: 線段座標1 (x,y)
:param line_point2: 線段座標2 (x,y)
:return:
"""
# 對於兩點座標為同一點時,返回點與點的距離
if line_point1 == line_point2:
point_array = np.array(point)
point1_array = np.array(line_point1)
return np.linalg.norm(point_array - point1_array)
# 計算直線的三個引數
A = line_point2[1] - line_point1[1]
B = line_point1[0] - line_point2[0]
C = (line_point1[1] - line_point2[1]) * line_point1[0] + \
(line_point2[0] - line_point1[0]) * line_point1[1]
# 根據點到直線的距離公式計算距離
distance = np.abs(A * point[0] + B * point[1] + C) / (np.sqrt(A ** 2 + B ** 2))
return distance
點到線段的最小距離
- 方法二、向量法、判斷為位置
- 先計算AD
當AD計算結束後,可以根據AD的相應的座標值得到D的座標,在計算CD的長度
判斷投影點D的位置
\[r=\frac{\overrightarrow{AP}.\overrightarrow{AB}}{|\overrightarrow{AB}|^2} \]如果情況是上圖1所示,那麼0<r<1;
圖2所示的情況,那麼r ≥1;
圖3所示的情況,那麼得到r ≤0;
根據r
不同,最短距離也不同
double PointToSegDist(double x, double y, double x1, double y1, double x2, double y2)
{
double cross = (x2 - x1) * (x - x1) + (y2 - y1) * (y - y1);
if (cross <= 0) return Math.Sqrt((x - x1) * (x - x1) + (y - y1) * (y - y1));
double d2 = (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1);
if (cross >= d2) return Math.Sqrt((x - x2) * (x - x2) + (y - y2) * (y - y2));
double r = cross / d2;
double px = x1 + (x2 - x1) * r;
double py = y1 + (y2 - y1) * r;
return Math.Sqrt((x - px) * (x - px) + (py - y) * (py - y));
}
def inner_prod(point1, point2):
# 計算 向量內積
# point1 (x,y)
# point2 (x,y)
# x1*x2+y1*y2+...
return point1[0] * point2[0] + point1[1] * point2[1]
def pointToSegDist(point, l1, l2):
# 計算點到線段的最小距離 (注意是線段)
# point (x,y)
# l1 (x,y)
# l2 (x,y)
# r ≤0, 最小長度為 l1point
# 0 < r < 1; 最小長度為 l2point
# r ≥1; point在 l1l2 上的投影
import math
x, y = l2[0] - l1[0], l2[1] - l1[1]
l1l2 = [x, y]
x, y = point[0] - l1[0], point[1] - l1[1]
l1point = [x, y]
cross = inner_prod(l1l2, l1point) # 計算內積(向量l1l2,向量l1point)
if (cross <= 0):
return math.sqrt((point[0] - l1[0]) ** 2 + (point[1] - l1[1]) ** 2)
d2 = (l2[0] - l1[0]) ** 2 + (l2[1] - l1[1]) ** 2
if cross >= d2:
return math.sqrt((point[0] - l2[0]) ** 2 + (point[1] - l2[1]) ** 2)
r = cross / d2
px = l1[0] + (l2[0] - l1[0]) * r
py = l1[1] + (l2[1] - l1[1]) * r
return np.sqrt((point[0] - px) ** 2 + (point[1] - py) ** 2)
point = [5, -5]
l1 = [0, 0]
l2 = [5, 0]
res = pointToSegDist(point, l1, l2)
print(res)
參考部落格: https://blog.csdn.net/angelazy/article/details/38489293
點線上段上
方法1,計算點到線段的最小距離
點與線段的關係點與線段只有兩種關係
- 線上段上面
- 線上段外面
方法2
判斷方法(滿足兩個條件)
- 點P與線段AB任意端點間的斜率與AB線段斜率相等,點P在直線AB上
- 確保給定點P的Y座標線上段AB兩個端點的Y座標之間, 確保線上段AB上
注意:當點P為AB端點時,不存在斜率,直接線上段上
def point_isonline(point, l1, l2):
D = 0
if point == l1 or point == l2:
return 1
try:
# 防止出現斜率不存在的情況
slope_pointl1 = round((l1[1] - point[1]) / (l1[0] - point[0]), 7)
slope_pointl2 = round((l2[1] - point[1]) / (l2[0] - point[0]), 7)
if slope_pointl1 == slope_pointl2 != 0:
if l1[0] < point[0] < l2[0] or l1[0] > point[0] > point[0]:
D = 1
else:
D = 0
elif slope_pointl1 == slope_pointl2 == 0:
if l1[1] < point[1] < l2[1] or l1[1] > point[1] > point[1]:
D = 1
else:
D = 0
else:
D = 0
except:
# 斜率不存在
if point[0] == l1[0] == l2[0] and l1[1] < point[1] < l2[1] or l1[1] > point[1] > point[1]:
D = 1
else:
D = 0
return D
# point = [0, 5]
# l1 = [0, 0]
# l2 = [5, 5]
# res = point_isonline(point, l1, l2)
# print(res)
線段和線段
假設有兩條線段AB,CD,若AB,CD相交,我們可以確定:
1.線段AB與CD所在的直線相交,即點A和點B分別在直線CD的兩邊;
2.線段CD與AB所在的直線相交,即點C和點D分別在直線AB的兩邊;
同時滿足是兩線段相交的充要條件,可以證明線段AB與CD相交
針對向量AB,CD 相交的衝要條件:
AB×AC > 0;向量AD在向量AB的順勢針方向,AB×AD < 0,兩叉乘結果異號
特殊情況
- AB CD 共線
- AB CD 共點(AB CD 共點、或者其中一點在 另一段線段上面)
AB×AD=0, 或者 AB×AC = 0
def segment(l1, l2, p3, p4):
'''
:param l1,l2: 線段AB 的起點和終點 l1(x,y) l2(x,y)
:param p3,p4: 線段CD 的起點和終點 p3(x,y) p4(x,y)
:return:
'''
# 判斷兩線段是否相交
# 依次判斷CD在AB的兩側,AB在CD的兩側, 需要叉乘的結果異號
cross_l1l2_l1p3 = cross(l1, l2, p3)
cross_l1l2_l1p4 = cross(l1, l2, p4)
cross_p3p4_p3l1 = cross(p3, p4, l1)
cross_p3p4_p3l2 = cross(p3, p4, l2)
if cross_l1l2_l1p3 * cross_l1l2_l1p4 <= 0 and cross_p3p4_p3l1 * cross_p3p4_p3l2 <= 0:
D = 1
else:
D = 0
return D
def cross(p0, p1, p2):
"""
向量a×向量b (×為向量叉乘)
向量 a (p1x-p0x,p1y-p0y) p1->p0 向量 b (p2x-p0x,p2y-p0y) p2->p0
:param p0: 起始點
:param p1: 終點1
:param p2: 終點2
:return:
"""
# 計算叉積
# 若結果小於0,表示向量b在向量a的順時針方向;
# 若結果大於0,表示向量b在向量a的逆時針方向;
# 若等於0,表示向量a與向量b 方向重合
x1 = p1[0] - p0[0]
y1 = p1[1] - p0[1]
x2 = p2[0] - p0[0]
y2 = p2[1] - p0[1]
return x1 * y2 - x2 * y1
l1=[0,0]
l2=[5,0]
p3=[2,-2]
p4=[2,2]
res=segment(l1, l2, p3, p4)
print(res)
參考文獻:https://www.cnblogs.com/tuyang1129/p/9390376.html
點與多邊形關係
判斷點是否處於多邊形內方法
方法一
叉乘判別法(只適用於凸多邊形)
將凸多邊形中每一個邊AB,與被測點P,求PA×PB
。判斷結果的符號是否發生變化,如果沒有變化,P在多邊形內;反之點處於凸多邊形外。
def cross(p0, p1, p2):
"""
向量a×向量b (×為向量叉乘)
向量 a (p1x-p0x,p1y-p0y) p1->p0 向量 b (p2x-p0x,p2y-p0y) p2->p0
:param p0: 起始點
:param p1: 終點1
:param p2: 終點2
:return:
"""
# 計算叉積
# 若結果小於0,表示向量b在向量a的順時針方向;
# 若結果大於0,表示向量b在向量a的逆時針方向;
# 若等於0,表示向量a與向量b 方向重合
x1 = p1[0] - p0[0]
y1 = p1[1] - p0[1]
x2 = p2[0] - p0[0]
y2 = p2[1] - p0[1]
return x1 * y2 - x2 * y1
def point_isin_poly(l1, l2, x1y1, x2y2, x3y3, x4y4):
# 檢測點 是否在多邊形內部
# 計算內部的一點和每個多邊形 線段的叉積 判斷叉積的符號是否相同
# 叉積=0在多邊形邊界上
cross1 = cross(l1, x1y1, x2y2)
cross2 = cross(l1, x2y2, x3y3)
cross3 = cross(l1, x3y3, x4y4)
cross4 = cross(l1, x4y4, x1y1)
print(cross1,cross2,cross3,cross4)
if (cross1 >= 0 and cross2 >= 0 and cross3 >= 0 and cross4 >= 0) or (
cross1 <= 0 and cross2 <= 0 and cross3 <= 0 and cross4 <= 0):
D = 1
else:
D = 0
return D
# l1=[5,1.5]
# l2=[5,0]
# x1y1, x2y2, x3y3, x4y4=[0,0],[0,2],[8,2],[8,0]
# res=point_isin_poly(l1, l2, x1y1, x2y2, x3y3, x4y4)
# print(res)
線段與多邊形的關係
class Line_Polygon_Relations():
def __init__(self):
pass
def check_line_poly(self, l1, l2, x1y1, x2y2, x3y3, x4y4):
'''
:param l1,l2: 線段 L 的 座標
:param x1y1: 多邊形 x1234 的座標 座標 順時針方向旋轉
:param x2y2:
:param x3y3:
:param x4y4:
:return:
'''
D = 0
pxy12 = self.segment(l1, l2, x1y1, x2y2) # 判斷線段是否和 多邊形線段相交
pxy23 = self.segment(l1, l2, x2y2, x3y3)
pxy34 = self.segment(l1, l2, x3y3, x4y4)
pxy41 = self.segment(l1, l2, x4y4, x1y1)
# 只能判斷是 線段和多邊形是否相交,如果在多邊形內部需要再次判斷
if pxy12 or pxy23 or pxy34 or pxy41:
D = 1
else:
# 是否在多邊形內部判斷
# 計算內部的一點和每個多邊形 線段的叉積 判斷叉積的符號是否相同
cross1 = self.cross(l1, x1y1, x2y2)
cross2 = self.cross(l1, x2y2, x3y3)
cross3 = self.cross(l1, x3y3, x4y4)
cross4 = self.cross(l1, x4y4, x1y1)
if (cross1 > 0 and cross2 > 0 and cross3 > 0 and cross4 > 0) or (
cross1 < 0 and cross2 < 0 and cross3 < 0 and cross4 < 0):
D = 1
else:
D = 0
return D
def segment(self, l1, l2, p3, p4):
'''
:param l1,l2: 線段AB 的起點和終點 l1(x,y) l2(x,y)
:param p3,p4: 線段CD 的起點和終點 p3(x,y) p4(x,y)
:return:
'''
# 判斷兩線段是否相交
# 依次判斷CD在AB的兩側,AB在CD的兩側
cross_l1p3_l2p3 = self.cross(l1, l2, p3)
cross_l1p4_l2p4 = self.cross(l1, l2, p4)
cross_p3l1_p4l1 = self.cross(p3, p4, l1)
cross_p3l2_p3l2 = self.cross(p3, p4, l2)
if cross_l1p3_l2p3 * cross_l1p4_l2p4 <= 0 and cross_p3l1_p4l1 * cross_p3l2_p3l2 <= 0:
D = 1
else:
D = 0
return D
def cross(self, p0, p1, p2):
"""
向量a×向量b (×為向量叉乘)
向量 a (p1x-p0x,p1y-p0y) p1->p0 向量 b (p2x-p0x,p2y-p0y) p2->p0
:param p1:
:param p2:
:param p3:
:return:
"""
# 計算叉積
# 若結果小於0,表示向量b在向量a的順時針方向;
# 若結果大於0,表示向量b在向量a的逆時針方向;
# 若等於0,表示向量a與向量b 方向重合
x1 = p1[0] - p0[0]
y1 = p1[1] - p0[1]
x2 = p2[0] - p0[0]
y2 = p2[1] - p0[1]
return x1 * y2 - x2 * y1
line_poly=Line_Polygon_Relations()
res = line_poly.check_line_poly((5, 4), (10, 2), (0, 0), (10, 10), (20, 5), (10, 0))