java

位置:IT落伍者 >> java >> 浏览文章

谈谈"求线段交点"的几种算法


发布日期:2018年12月01日
 
谈谈"求线段交点"的几种算法

求线段交点是一种非常基础的几何计算 在很多游戏中都会被使用到

下面我就现学现卖的把最近才学会的一些求线段交点的算法说一说 希望对大家有所帮助

本文讲的内容都很初级 主要是面向和我一样的初学者 所以请各位算法帝们轻拍啊 嘎嘎

引用已知线段(ab) 和线段(cd) 其中a b c d为端点 求线段交点p (平行或共线视作不相交)

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

求直线交点时 我们可通过直线的一般方程 ax+by+c= 求得(方程中的abc为系数不是前面提到的端点另外也可用点斜式方程和斜截式方程此处暂且不论)

然后根据交点的与线段端点的位置关系来判断交点是否在线段上 公式如下图

【责编:at

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

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

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

点a和点b在线段cd法线上的投影如图所示 这时候我们还要做一次线段cd在自己法线上的投影(选择点c或点d中的一个即可)

主要用来做参考

图中点a投影和点b投影在点c投影的两侧 说明线段ab的端点在线段cd的两侧

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

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

求线段ab的法线

JavaScript Code复制内容到剪贴板var nx=by ayny=ax bxvar normalLine = { x nx y ny }注意 其中 normalLinex和normalLiney的几何意义表示法线的方向 而不是坐标

求点c在法线上的投影位置

JavaScript Code复制内容到剪贴板var dist= normalLinex*cx + normalLiney*cy

注意 这里的投影位置是一个标量 表示的是到法线原点的距离 而不是投影点的坐标

通常知道这个距离就足够了

当我们把图中 点a投影(distA)点b投影(distB)点c投影(distC) 都求出来之后 就可以很容易的根据各自的大小判断出相对位置

distA==distB==distC 时 两条线段共线distA==distB!=distC 时 两条线段平行distA 和 distB 在distC 同侧时 两条线段不相交

distA 和 distB 在distC 异侧时 两条线段是否相交需要再判断点c点d与线段ab的关系

前面的那些步骤 只是实现了判断线段是否相交 当结果为true时 我们还需要进一步求交点

求交点的过程后面再说 先看一下该算法的完整实现

JavaScript Code复制内容到剪贴板function segmentsIntr(a b c d){

//线段ab的法线N var nx = (by ay) ny = (ax bx)

//线段cd的法线N var nx = (dy cy) ny = (cx dx)

//两条法线做叉乘 如果结果为 说明线段ab和线段cd平行或共线不相交var denominator = nx*ny ny*nxif (denominator==) { return false}

//在法线N上的投影var distC_N=nx * cx + ny * cyvar distA_N=nx * ax + ny * aydistC_Nvar distB_N=nx * bx + ny * bydistC_N

// 点a投影和点b投影在点c投影同侧 (对点在线段上的情况本例当作不相交处理)if ( distA_N*distB_N>= ) { return false}

// //判断点c点d 和线段ab的关系 原理同上// //在法线N上的投影var distA_N=nx * ax + ny * ayvar distC_N=nx * cx + ny * cydistA_Nvar distD_N=nx * dx + ny * dydistA_Nif ( distC_N*distD_N>= ) { return false}

//计算交点坐标var fraction= distA_N / denominatorvar dx= fraction * nydy= fraction * nxreturn { x ax + dx y ay + dy }}

最后 求交点坐标的部分 所用的方法看起来有点奇怪 有种摸不着头脑的感觉

其实它和算法一 里面的算法是类似的只是里面的很多计算项已经被提前计算好了

换句话说 算法二里求交点坐标的部分 其实也是用的直线的线性方程组来做的

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

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

【责编:at

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

(咦? 怎么感觉和算法二一样啊? 不要怀疑 确实一样 …… 囧)

所谓算法三 其实只是对算法二的一个改良 改良的地方主要就是 不通过法线投影来判断点和线段的位置关系 而是通过点和线段构成的三角形面积来判断

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

JavaScript Code复制内容到剪贴板var triArea=( (ax cx) * (by cy) (ay cy) * (bx cx) ) /

因为 两向量叉乘==两向量构成的平行四边形(以两向量为邻边)的面积 所以上面的公式也不难理解

而且由于向量是有方向的 所以面积也是有方向的 通常我们以逆时针为正 顺时针为负数

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

【责编:at

上一篇:为测试 Java 应用程序生成证书链

下一篇:Java调试器--jdb.exe