计算几何
点、线段、多边形
计算几何
点积
向量的内积(点乘/数量积)
注意:点乘的结果是一个标量
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))
原文地址:https://www.cnblogs.com/tian777/p/15131857.html
- 你可能需要为你的 APP 适配 iOS 11
- Golang语言 redis 使用
- 页面结构化在 Android 上的尝试
- iOS 11: CORE ML—浅析
- 高性能网络编程7--tcp连接的内存使用
- Android 平台 Native 代码的崩溃捕获机制及实现
- go语言变参,匿名函数的多种用法
- 问题帖子--Concurrent Read/Write Map
- Android 混淆那些事儿
- H5 直播避坑指南
- H5 和移动端 WebView 缓存机制解析与实战
- 根据IE版本加载不同CSS样式的方法小结,解决低版本IE兼容问题
- Linux下用dd命令测试硬盘的读写速度
- 教你 Debug 的正确姿势——记一次 CoreMotion 的 Crash
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 如何将第三方服务注册集成到 Istio ?
- 【Pod Terminating原因追踪系列】之 containerd 中被漏掉的 runc 错误信息
- 【Pod Terminating原因追踪系列之二】exec连接未关闭导致的事件阻塞
- CD+服务网格灰度发布实践,一文带你体验如何编排更灵活
- 花十分钟的时间武装你的代码库
- 对HTML-input的一些思考和理解
- 【投稿】刀哥:Rust学习笔记 1
- 【Rust日报】2020-08-13 关于群集(Bevy)引擎ECS框架中system的语法糖是怎么实现的
- 最新情报:所有的递归都可以改写成非递归?
- 算法篇:树之转换为二叉搜索树
- 算法篇:树之倒数k个节点
- 揭开链表的真面目
- Coder,我怀疑你并不会枚举
- 掌握坐标轴的log转换
- 连通域的原理与Python实现