Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >几何算法:判断两条线段是否相交

几何算法:判断两条线段是否相交

作者头像
前端西瓜哥
发布于 2023-08-18 05:27:52
发布于 2023-08-18 05:27:52
99700
代码可运行
举报
运行总次数:0
代码可运行

‍大家好,我是前端西瓜哥。

如何判断两条线段(注意不是直线)是否有交点?

传统几何算法的局限

上过一点学的西瓜哥我,只用高中学过的知识,还是可以解这个问题的。

一条线段两个点,可以列出一个两点式(x - x1) / (x2 - x1) = (y - y1) / (y2 - y1)),两条线段是两个两点式,这样就是 二元一次方程组 了 ,就能求出两条直线的交点。

然后判断这个点是否在其中一条线段上。如果在,说明两线段相交,否则不相交。

看起来不错,但这里要考虑直线垂直或水平于坐标轴的特殊情况,还有两条直线平行导致没有唯一解的情况,除数不能为 0 的情况。

特殊情况实在是太多了,能用是能用,但不好用。

那么,有其他的更好的解法吗?

有的,叉乘。

叉乘是什么?

叉乘(cross product)是线性代数的一个概念,也叫外积、叉积、向量积,是在三维空间中两个向量的二元运算的结果,该结果为一个向量。

但那是严格意义上的。实际也可以用在二维空间的二维向量中,不过此时它们的叉乘结果变成了标量。

假设向量 A 为 (x1, y1),向量 B 为 (x2, y2),则叉乘 AxB 的结果为 x1 * y2 - x2 * y1

(注意叉乘不满足交换律)

在几何意义上,这个叉乘结果的绝对值对应两个向量组成的平行四边形的面积。

此外可通过符号判断向量 A 变成向量 B 的旋转方向。

如果叉乘为正数,说明 A 变成 B 需要逆时针旋转(旋转角度小于 180 度);

如果为负数,说明 A 到 B 需要顺时针旋转;

如果为 0,说明两个向量平行(或重合)

叉乘解法的原理

回到题目本身。

假设线段 1 的端点为 A 和 B,线段 2 的端点为 C 和 D。

我们可以换另一个角度去解,即判断线段 1 的两个端点是否在线段 2 的两边,然后再反过来比线段 2 的两点是否线段 1 的两边。

这里我们可以利用上面 叉乘的正负代表旋转方向的特性

以上图为例, AB 向量到 AD 向量位置需要逆时针旋转,AB 向量到 AC 向量则需要顺时针,代表 C 和 D 在 AB 的两侧,对应就是两个叉乘相乘为负数。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
function crossProduct(p1: Point, p2: Point, p3: Point): number {
  const x1 = p2[0] - p1[0];
  const y1 = p2[1] - p1[1];
  const x2 = p3[0] - p1[0];
  const y2 = p3[1] - p1[1];
  return x1 * y2 - x2 * y1;
}

const [a, b] = seg1;
const [c, d] = seg2;

// d1 的符号表示 AB 旋转到 AC 的旋转方向
const d1 = crossProduct(a, b, c);

只是判断了 C 和 D 在 AB 线段的两侧还不行,因为可能还有下面这种情况。

所以我们还要再判断一下,A 和 B 是否在 CD 线的的两侧。计算过程同上,这里不赘述。

一般实现

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
type Point = [number, number];

function crossProduct(p1: Point, p2: Point, p3: Point): number {
  const x1 = p2[0] - p1[0];
  const y1 = p2[1] - p1[1];
  const x2 = p3[0] - p1[0];
  const y2 = p3[1] - p1[1];
  return x1 * y2 - x2 * y1;
}

function isSegmentIntersect(
  seg1: [Point, Point],
  seg2: [Point, Point],
): boolean {
  const [a, b] = seg1;
  const [c, d] = seg2;

  const d1 = crossProduct(a, b, c);
  const d2 = crossProduct(a, b, d);
  const d3 = crossProduct(c, d, a);
  const d4 = crossProduct(c, d, b);

  return d1 * d2 < 0 && d3 * d4 < 0;
}

// 测试
const seg1: [Point, Point] = [
  [0, 0],
  [1, 1],
];
const seg2: [Point, Point] = [
  [0, 1],
  [1, 0],
];

console.log(isSegmentIntersect(seg1, seg2)); // true

注意,这个算法认为线段的端点刚好在另一条线段上的情况,不属于相交。

考虑点在线段上或重合

如果你需要考虑线段的端点刚好在另一条线段上的情况,需要额外在叉乘为 0 的情况下,再判断一下线段 1 的端点是否在另一个线段的 x 和 y 范围内。

对应的算法实现:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
type Point = [number, number];

function crossProduct(p1: Point, p2: Point, p3: Point): number {
  const x1 = p2[0] - p1[0];
  const y1 = p2[1] - p1[1];
  const x2 = p3[0] - p1[0];
  const y2 = p3[1] - p1[1];
  return x1 * y2 - x2 * y1;
}

function onSegment(p: Point, seg: [Point, Point]): boolean {
  const [a, b] = seg;
  const [x, y] = p;
  return (
    x >= Math.min(a[0], b[0]) &&
    x <= Math.max(a[0], b[0]) &&
    y >= Math.min(a[1], b[1]) &&
    y <= Math.max(a[1], b[1])
  );
}

function isSegmentIntersect(
  seg1: [Point, Point],
  seg2: [Point, Point],
): boolean {
  const [a, b] = seg1;
  const [c, d] = seg2;

  const d1 = crossProduct(a, b, c);
  const d2 = crossProduct(a, b, d);
  const d3 = crossProduct(c, d, a);
  const d4 = crossProduct(c, d, b);

  if (d1 * d2 < 0 && d3 * d4 < 0) {
    return true;
  }
 
  // d1 为 0 表示 C 点在 AB 所在的直线上
  // 接着会用 onSegment 再判断这个 C 是不是在 AB 的 x 和 y 的范围内
  if (d1 === 0 && onSegment(c, seg1)) return true;
  if (d2 === 0 && onSegment(d, seg1)) return true;
  if (d3 === 0 && onSegment(a, seg2)) return true;
  if (d4 === 0 && onSegment(b, seg2)) return true;

  return false;
}

// 测试
const seg1: [Point, Point] = [
  [0, 0],
  [1, 1],
];
const seg2: [Point, Point] = [
  [0, 1],
  [1, 0],
];
const seg3: [Point, Point] = [
  [0, 0],
  [2, 2],
];
const seg4: [Point, Point] = [
  [1, 1],
  [1, 0],
];
// 普通相交情况
console.log(isSegmentIntersect(seg1, seg2)); //  true
// 线段 1 的一个端点刚好在线段 2 上
console.log(isSegmentIntersect(seg3, seg4)); // true

结尾

总结一下,判断两条线段是否相交,可以判断两条线段的两端点是否分别在各自的两侧,对应地需要用到二维向量叉乘结果的正负值代表向量旋转方向的特性。

我是前端西瓜哥,关注我,学习更多几何算法。


相关阅读,

几何算法:矩形碰撞和包含检测算法

在容器内显示图片的五种方案:contain、cover、fill、none、scale-down

计算机图形学:变换矩阵

求向量的角度

图形编辑器开发:以光标为中心缩放画布

图形编辑器开发:参考线吸附效功能,让图形自动对齐

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2023-07-16,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 前端西瓜哥 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
图形编辑器开发:基于相交策略选中图形
我开发的图形编辑器,原本选中图形是基于选区是否完全包含对应图形来判断其是否被选中,使用的是矩形包含判断。
前端西瓜哥
2023/08/18
2000
图形编辑器开发:基于相交策略选中图形
在两条直线相交处添加圆角,算法该如何实现?
然后基于圆心作两条直线的垂足得到两个点,这两个点就是圆弧起点和终点,然后确定方向就可以了。
前端西瓜哥
2024/06/17
2540
在两条直线相交处添加圆角,算法该如何实现?
解析几何:计算两条线段的交点
我们要实现 getLineSegIntersection 方法:提供两条线段,计算它们的交点。
前端西瓜哥
2023/10/22
4830
解析几何:计算两条线段的交点
位置和方向的世界,计算几何的基本问题
本文从最基本的线段相交问题出发,从解析几何进入计算几何,介绍点积和叉积这个最基本的计算几何工具,引入计算几何这个关于位置和方向的大航海世界~
ACM算法日常
2020/06/29
9300
计算几何之求两线段的交点
思路就是连接线段的端点,构造向量,从而构造出相似三角形,然后求出交点在一条线段上的位置(用比例t来表示),然后再加到线段端点上就可以了。
灯珑LoGin
2022/10/31
9800
WPF 基础 2D 图形学知识 判断点是否在线段上
本文算法属于通用的算法,可以在 WPF 和 UWP 和 Xamarin 等上运行,基本上所有的 .NET 平台都能执行
林德熙
2021/03/23
7410
WPF 基础 2D 图形学知识 判断点是否在线段上
unity3d:向量计算,AOE图形相交
x0为起点,u为单位向量,则x0t的长度为 |x0x|cosa = x0xu / |u|,因为u为单位向量,模长为1。然后得到t点坐标为x - (x0 + Mathf.Abs(t) * u),因为x可能在x0的左边,所以只算长度的绝对值单位向量,然后算x,t两点距离
立羽
2023/08/24
3440
unity3d:向量计算,AOE图形相交
ACM刷题之路(六)直线相交问题 POJ3304 Segments
Given n segments in the two dimensional space, write a program, which determines if there exists a line such that after projecting these segments on it, all projected segments have at least one point in common.
Designer 小郑
2023/07/31
1670
ACM刷题之路(六)直线相交问题 POJ3304 Segments
计算几何之【映像】
求映像就要先求出p在线段上的投影点pp,然后把p投影点pp的向量放大两倍并与p点坐标相加,那么就求出了点x
灯珑LoGin
2022/10/31
5790
外卖小哥
缘起 大家知道 外卖小哥都是很辛苦的. 所以他们巴不得从接单处到用户住处能以最短路到达. 你能帮帮他们吗? Uva 248 Cutting Corners 分析 平面上有一些矩形建筑物,一个人骑车从起
ACM算法日常
2020/07/20
7940
外卖小哥
计算几何之【投影】
把p与线段上的一个点相连,求连接生成的这个向量在线段上的投影的长度,然后用这个投影的长度与线段长度作比,再对x,y坐标分别加上去就可以了。
灯珑LoGin
2022/10/31
4890
计算几何之线段相交问题(平面扫描)
求n条线段的交点,可以用抽选配对的方式来遍历所有的情况,这样子时间复杂度为O(n2).
灯珑LoGin
2022/10/31
1K0
计算几何之线段相交问题(平面扫描)
计算几何笔记
若向量$(x, y)$旋转角度为$a$,则旋转后的向量为$(xcosa - ysina, y cosa + xsina)$
attack
2018/08/01
1.3K0
计算几何笔记
计算几何之判断线段相交
两条线段都满足“另一条线段的两个端点分别位于当前线段的顺时针方向和逆时针方向”,那么这两条线段相交。
灯珑LoGin
2022/10/31
5030
平面几何:求直线线段的轮廓线
求线段的法向量,乘以线宽的一半,得到位移向量。然后让线段的两个点分别做两个方向的位移,得到多边形的 4 个顶点,将它们按照一定顺序连接起来得到多边形,这个多边形就是我们要求的轮廓多边形。
前端西瓜哥
2024/07/31
1220
平面几何:求直线线段的轮廓线
POJ 1066--Treasure Hunt(判断线段相交)
Treasure Hunt Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 7857 Accepted: 3247 Description
Enterprise_
2019/02/20
3270
ACM计算几何篇_acm数学
https://linxi99.gitee.io/20190211/ACM计算几何篇/
全栈程序员站长
2022/11/19
1.4K0
ACM计算几何篇_acm数学
如何用 canvas 画出分形图
分形是一门以非规则几何形态为研究对象的几何学,由曼德勃 罗(B.B.Mandelbrot)等人创立并命名。
ConardLi
2021/09/29
2K0
如何用 canvas 画出分形图
【Unity面试篇】Unity 面试题总结甄选 |算法相关 | ❤️持续更新❤️
进阶答案 检测数字的二进制最低位是否为0。将最低位和1相与,如果结果为0,则为偶数,否则为奇数。
呆呆敲代码的小Y
2023/07/24
9090
【Unity面试篇】Unity 面试题总结甄选 |算法相关 | ❤️持续更新❤️
计算几何算法概览
  计算机的出现使得很多原本十分繁琐的工作得以大幅度简化,但是也有一些在人们直观看来很容易的问题却需要拿出一套并不简单的通用解决方案,比如几何问题。作为计算机科学的一个分支,计算几何主要研究解决几何问题的算法。在现代工程和数学领域,计算几何在图形学、机器人技术、超大规模集成电路设计和统计等诸多领域有着十分重要的应用。在本文中,我们将对计算几何常用的基本算法做一个全面的介绍,希望对您了解并应用计算几何的知识解决问题起到帮助。
owent
2023/03/05
1.7K0
相关推荐
图形编辑器开发:基于相交策略选中图形
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验