线段求交点的算法

算法一: 求两条线段所在直线的交点, 再判断交点是否在两条线段上.

求直线交点时 我们可通过直线的一般方程 ax+by+c=0 求得(方程中的abc为系数,不是前面提到的端点,另外也可用点斜式方程和斜截式方程,此处暂且不论). 
然后根据交点的与线段端点的位置关系来判断交点是否在线段上. 公式如下图:

算法一思路比较清晰易懂, 但是性能并不高. 因为它在不确定交点是否有效(在线段上)之前, 就先去计算了交点, 耗费了较多的时间. 
如果最后发现交点无效, 那么之前的计算就白折腾了. 而且整个计算的过程也很复杂. 
那么有没有一种思路,可以让我们先判断是否存在有效交点,然后再去计算它呢? 
显然答案是肯定的. 于是就有了后面的一些算法.

算法二: 判断每一条线段的两个端点是否都在另一条线段的两侧, 是则求出两条线段所在直线的交点, 否则不相交.

第一步判断两个点是否在某条线段的两侧, 通常可采用投影法:

求出线段的法线向量, 然后把点投影到法线上, 最后根据投影的位置来判断点和线段的关系. 见下图

点a和点b在线段cd法线上的投影如图所示, 这时候我们还要做一次线段cd在自己法线上的投影(选择点c或点d中的一个即可). 
主要用来做参考. 
图中点a投影和点b投影在点c投影的两侧, 说明线段ab的端点在线段cd的两侧.

同理, 再判断一次cd是否在线段ab两侧即可.

求法线 , 求投影 什么的听起来很复杂的样子, 实际上对于我来说也确实挺复杂,在几个月前我也不会(念书那会儿的几何知识都忘光了 :‘( )‘ 
不过好在学习和实现起来还不算复杂, 皆有公式可循:

最后 求交点坐标的部分 所用的方法看起来有点奇怪, 有种摸不着头脑的感觉. 
其实它和算法一 里面的算法是类似的,只是里面的很多计算项已经被提前计算好了. 
换句话说, 算法二里求交点坐标的部分 其实也是用的直线的线性方程组来做的.

现在来简单粗略 很不科学的对比一下算法一和算法二: 
1 最好情况下, 两种算法的复杂度相同 
2 最坏情况, 算法一和算法二的计算量差不多 
3 但是算法二提供了 更多的"提前结束条件",所以平均情况下,应该算法二更优.

实际测试下来, 实际情况也确实如此.

前面的两种算法基本上是比较常见的可以应付绝大多数情况. 但是事实上还有一种更好的算法. 
这也是我最近才新学会的(我现学现卖了,大家不要介意啊...)

=============================== 
算法三: 判断每一条线段的两个端点是否都在另一条线段的两侧, 是则求出两条线段所在直线的交点, 否则不相交.

(咦? 怎么感觉和算法二一样啊? 不要怀疑 确实一样 ... 囧) 
所谓算法三, 其实只是对算法二的一个改良, 改良的地方主要就是 : 
不通过法线投影来判断点和线段的位置关系, 而是通过点和线段构成的三角形面积来判断.

先来复习下三角形面积公式: 已知三角形三点a(x,y) b(x,y) c(x,y), 三角形面积为:

Javascript代码  

  1. var triArea=( (a.x - c.x) * (b.y - c.y) - (a.y - c.y) * (b.x - c.x) ) /2 ;

因为 两向量叉乘==两向量构成的平行四边形(以两向量为邻边)的面积 , 所以上面的公式也不难理解. 
而且由于向量是有方向的, 所以面积也是有方向的, 通常我们以逆时针为正, 顺时针为负数.

改良算法关键点就是: 
如果"线段ab和点c构成的三角形面积"与"线段ab和点d构成的三角形面积" 构成的三角形面积的正负符号相异, 
那么点c和点d位于线段ab两侧. 如下图所示:

图中虚线所示的三角形, 缠绕方向(三边的定义顺序)不同, 所以面积的正负符号不同.

算法三在算法二的基础上, 大大简化了计算步骤, 代码也更精简. 可以说,是三种算法里, 最好的.实际测试结果也是如此.

当然必须坦诚的来说, 在Javascript里, 对于普通的计算, 三种算法的时间复杂度其实是差不多的(尤其是V8引擎下). 
我的测试用例里也是进行变态的百万次级别的线段相交测试 才能拉开三种算法之间的差距.

摘自:http://fins.iteye.com/blog/1522259

时间: 2024-10-10 23:34:54

线段求交点的算法的相关文章

两线段相交求交点

算法一 #include #include #include struct POINT { int x; int y; }; bool IsLineSegmentCross(POINT pFirst1, POINT pFirst2, POINT pSecond1, POINT pSecond2) { //每个线段的两点都在另一个线段的左右不同侧,则能断定线段相交 //公式对于向量(x1,y1)->(x2,y2),判断点(x3,y3)在向量的左边,右边,还是线上. //p=x1(y3-y2)+x2

求直线与线段的交点

1,求点到直线的带符号距离: float getSignedDistance(点P,直线AB) //求点P到直线AB的带符号距离(当P在AB左侧时距离为正,右侧时为负) { dir=直线AB的方向向量 根据dir求出直线AB的左手法线向量leftNormal = (-dir.y,dir.x).normalized 线段AP在leftNormal上的投影即为P到直线AB的带符号距离: signedDistance=dot(AP,leftNormal); return signedDistance;

Pipe - POJ 1039(线段相交交点)

题目大意:有一个不反光并且不透光的管道,现在有一束光线从最左端进入,问能达到的最右端是多少,输出x坐标. 分析:刚开始做是直接枚举两个点然后和管道进行相交查询,不过这样做需要考虑的太多,细节不容易掌控.后来发现其实只需要对接口进行一下相交查询就简单多了,因为只需要考虑能不能通过每个截口就可以了,而且这样做的好处还有没有平行线和重叠线的情况,因为所有的截口都是垂直于x轴的,换一种想法海阔太空啊. 代码如下: =============================================

[CodeForces-1036E] Covered Points 暴力 GCD 求交点

题意: 在二维平面上给出n条不共线的线段,问这些线段总共覆盖到了多少个整数点 解法: 用GCD可求得一条线段覆盖了多少整数点,然后暴力枚举线段,求交点,对于相应的 整数交点,结果-1即可 1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<iostream> 5 #include<cmath> 6 #include<vector> 7 #inc

求凸包—— graham_scan算法

求凸包—— graham_scan算法 先按Y-X排序,在按对p0的极角排序,然后进行扫描 Point stk[maxn]; int top; bool cmpYX(const Point A,const Point B)//按Y-X排序 { if(A.y<B.y) return true; if(A.y==B.y){ return A.x<=B.x; } return false; } bool cmp(const Point A,const Point B)//按极角排序 { return

C语言求质数的算法

前言 上次被出了一题质数的C语言求解题目(面试),当时用了最粗暴的算法,回来仔细参考资料,其实答案有很多种: 1,小学生版本: 判断 x 是否为质数,就从 2 一直算到 x-1. static rt_uint32_t array1[ARRAY_LEN]; void func1(void) { for (rt_uint32_t i = 1; i <= ARRAY_LEN; i++) { array1[i - 1] = 0; } rt_uint32_t x, y = 0, z = 0; rt_uin

求赫夫曼编码的算法

求赫夫曼编码的算法 参考清华大学出版社出版的<数据结构(c语言版)>一书,在java下实现 //数据结构 class HuffmanNode{ public int weight;//权重 public int parent,lchild,rchild;//父节点.孩子节点在数组中的下标位置 public HuffmanNode(int weight,int parent,int lchild,int rchild){ this.weight = weight; this.parent = p

POJ1408-Fishnet(线段求交)

Fishnet Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 1788   Accepted: 1144 Description A fisherman named Etadokah awoke in a very small island. He could see calm, beautiful and blue sea around the island. The previous night he had enc

更快的求整数幂算法

相信整数幂运算作为一个算法演变的例子是再合适不过的了为了节省访客们宝贵的学习时间省去介绍递归等可能涉及到的初级概念的定义.同时如果发现文中有错误的地方请敞开衣服指正. 因为在测试性能时合适的测试数据是必要的,所以本文用C++的大数类进行演示. 点击获取C++大数类源码 这里我们先列一下会提到的算法分析技术: 动态规划 减治法 测试平台: Linux g++ 4.7 原始递归方法 这就不花时间赘述什么了. BigInteger pow(BigInteger x, int N) { if (N ==