计算几何-点线多边形位置位置关系
点、线段、多边形位置关系
计算几何
点积
向量的内积(点乘/数量积)
注意:点乘的结果是一个标量
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,距离就是垂线段的长度;如果没有,则距离转化为点到线段两端点的距离。
原理
求解直线方程
点到直线的距离公式
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的位置
如果情况是上图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))