问题描述

问题描述:已知两条线段P1P2和Q1Q2,判断P1P2和Q1Q2是否相交,若相交,求出交点。

两条线段的位置关系可以分为三类:有重合部分、无重合部分但有交点、无交点。

算法核心

算法的步骤如下:

1.快速排斥实验。

设以线段P1P2为对角线的矩形为R,设以线段Q1Q2为对角线的矩形为T,如果R和T不相交,则两线段不相交。

所以P1P2和Q1Q2相交的必要条件是以他们为对角线的矩形相交,即:

min(p1.x,p2.x) <= max(q1.x,q2.x) &&
min(q1.x,q2.x) <= max(p1.x,p2.x) &&
min(p1.y,p2.y) <= max(q1.y,q2.y) &&
min(q1.y,q2.y) <= max(p1.y,p2.y);

2.跨立实验。

如果两线段相交,则两线段必然相互跨立对方。
线段的跨立究竟是什么意思?向量的跨立是什么意思?

a、若P1P2跨立Q1Q2,则矢量(P1-Q1)和(P2-Q1)位于矢量(Q2-Q1)的两侧,即( P1 - Q1 ) × ( Q2 - Q1 ) * ( P2 - Q1 ) × ( Q2 - Q1 ) < 0。

等价于
(Q1.x-P1.x,Q1.y-P1.y) × ( Q1.x-Q2.x,Q1.y-Q2.y ) * ( Q1.x-P2.x,Q1.y-P2.y ) × ( Q1.x-Q2.x,Q1.y-Q2.y ) < 0
等价于
((Q1.x-P1.x)*(Q1.y-Q2.y)-(Q1.y-P1.y)*( Q1.x-Q2.x)) * ((Q1.x-P2.x)*(Q1.y-Q2.y)-(Q1.y-P2.y)*(Q1.x-Q2.x)) < 0

b、若Q1Q2跨立P1P2,则矢量(Q1-P1)和(Q2-P1)位于矢量(P2-P1)的两侧,即( Q1 - P1 ) × ( P2 - P1 ) * ( Q2 - P1 ) × ( P2 - P1 ) < 0。
等价于
(P1.x-Q1.x,P1.y-Q1.y) × ( P1.x-P2.x,P1.y-P2.y ) * ( P1.x-Q2.x,P1.y-Q2.y ) × ( P1.x-P2.x,P1.y-P2.y ) < 0
等价于
((P1.x-Q1.x)*(P1.y-P2.y)-(P1.y-Q1.y)*(P1.x-P2.x)) * ((P1.x-Q2.x)*(P1.y-P2.y)-(P1.y-Q2.y)*( P1.x-P2.x)) < 0

a和b两个不等式只要满足一个即可。

排斥实验和跨立实验的示例如下图所示。

代码

//跨立判断
bool isLineSegmentCross(const Point &P1,const Point &P2,const Point &Q1,const Point &Q2)
{if(((Q1.x-P1.x)*(Q1.y-Q2.y)-(Q1.y-P1.y)*( Q1.x-Q2.x)) * ((Q1.x-P2.x)*(Q1.y-Q2.y)-(Q1.y-P2.y)*(Q1.x-Q2.x)) < 0 ||((P1.x-Q1.x)*(P1.y-P2.y)-(P1.y-Q1.y)*(P1.x-P2.x)) * ((P1.x-Q2.x)*(P1.y-P2.y)-(P1.y-Q2.y)*( P1.x-P2.x)) < 0) return true;elsereturn false;
}

3.计算交点。

当判定两条线段相交后,可以进行交点的求解,求交点可以用平面几何方法,列点斜式方程来完成。但由于点斜式方程难以处理斜率为0的特殊情况,不方便求解。因而,参用向量法求解交点。

设交点为(x0,y0),则下列方程组成立:

根据以上方程组,消除参数k1和k2,得到如下方程:

然后求解(x0,y0),结果如下所示:

#include<stdio.h>
#define N 10002/**
算法适用于整形点,不适用于浮点型
**/typedef struct Point
{int x;int y;
}Point;double min(int x, int y)
{return x<y?x:y;
}double max(int x, int y)
{return x>y?x:y;
}//排斥实验
bool IsRectCross(const Point &p1,const Point &p2,const Point &q1,const Point &q2)
{bool ret = min(p1.x,p2.x) <= max(q1.x,q2.x)    &&min(q1.x,q2.x) <= max(p1.x,p2.x) &&min(p1.y,p2.y) <= max(q1.y,q2.y) &&min(q1.y,q2.y) <= max(p1.y,p2.y);return ret;
}/**这段代码不能得到正确答案,故注释
//跨立判断
bool IsLineSegmentCross(const Point &pFirst1,const Point &pFirst2,const Point &pSecond1,const Point &pSecond2)
{long line1,line2;line1 = pFirst1.x * (pSecond1.y - pFirst2.y) +pFirst2.x * (pFirst1.y - pSecond1.y) +pSecond1.x * (pFirst2.y - pFirst1.y);line2 = pFirst1.x * (pSecond2.y - pFirst2.y) +pFirst2.x * (pFirst1.y - pSecond2.y) + pSecond2.x * (pFirst2.y - pFirst1.y);if (((line1 ^ line2) >= 0) && !(line1 == 0 && line2 == 0))return false;line1 = pSecond1.x * (pFirst1.y - pSecond2.y) +pSecond2.x * (pSecond1.y - pFirst1.y) +pFirst1.x * (pSecond2.y - pSecond1.y);line2 = pSecond1.x * (pFirst2.y - pSecond2.y) + pSecond2.x * (pSecond1.y - pFirst2.y) +pFirst2.x * (pSecond2.y - pSecond1.y);if (((line1 ^ line2) >= 0) && !(line1 == 0 && line2 == 0))return false;return true;
}
**///跨立判断
bool IsLineSegmentCross(const Point &P1,const Point &P2,const Point &Q1,const Point &Q2)
{if(((Q1.x-P1.x)*(Q1.y-Q2.y)-(Q1.y-P1.y)*( Q1.x-Q2.x)) * ((Q1.x-P2.x)*(Q1.y-Q2.y)-(Q1.y-P2.y)*(Q1.x-Q2.x)) < 0 ||((P1.x-Q1.x)*(P1.y-P2.y)-(P1.y-Q1.y)*(P1.x-P2.x)) * ((P1.x-Q2.x)*(P1.y-P2.y)-(P1.y-Q2.y)*( P1.x-P2.x)) < 0) return true;elsereturn false;
}/**
求线段P1P2与Q1Q2的交点。
先进行快速排斥实验和跨立实验确定有交点再进行计算。
交点(x,y)使用引用返回。
没有验证过
**/
bool GetCrossPoint(const Point &p1,const Point &p2,const Point &q1,const Point &q2,long &x,long &y)
{if(IsRectCross(p1,p2,q1,q2)){if (IsLineSegmentCross(p1,p2,q1,q2)){//求交点long tmpLeft,tmpRight;tmpLeft = (q2.x - q1.x) * (p1.y - p2.y) - (p2.x - p1.x) * (q1.y - q2.y);tmpRight = (p1.y - q1.y) * (p2.x - p1.x) * (q2.x - q1.x) + q1.x * (q2.y - q1.y) * (p2.x - p1.x) - p1.x * (p2.y - p1.y) * (q2.x - q1.x);x = (int)((double)tmpRight/(double)tmpLeft);tmpLeft = (p1.x - p2.x) * (q2.y - q1.y) - (p2.y - p1.y) * (q1.x - q2.x);tmpRight = p2.y * (p1.x - p2.x) * (q2.y - q1.y) + (q2.x- p2.x) * (q2.y - q1.y) * (p1.y - p2.y) - q2.y * (q1.x - q2.x) * (p2.y - p1.y); y = (int)((double)tmpRight/(double)tmpLeft);return true;}}return false;
}

代码2-线段求交模板
http://www.cppblog.com/wicbnu/archive/2009/08/24/94225.html

#include <stdio.h>
#include <math.h>
const int N = 100010;
int mark[N];
struct Point
{double x,y;
};
struct stline
{Point a,b;
} line1,line2, p[N];int dblcmp(double a,double b)
{if (fabs(a-b)<=1E-6) return 0;if (a>b) return 1;else return -1;
}
//***************点积判点是否在线段上***************
double dot(double x1,double y1,double x2,double y2) //点积
{return x1*x2+y1*y2;
}int point_on_line(Point a,Point b,Point c) //求a点是不是在线段bc上,>0不在,=0与端点重合,<0在。
{return dblcmp(dot(b.x-a.x,b.y-a.y,c.x-a.x,c.y-a.y),0);
}
//**************************************************
double cross(double x1,double y1,double x2,double y2)
{return x1*y2-x2*y1;
}
double ab_cross_ac(Point a,Point b,Point c) //ab与ac的叉积
{return cross(b.x-a.x,b.y-a.y,c.x-a.x,c.y-a.y);
}
int ab_cross_cd (Point a,Point b,Point c,Point d) //求ab是否与cd相交,交点为p。1规范相交,0交点是一线段的端点,-1不相交。
{double s1,s2,s3,s4;int d1,d2,d3,d4;Point p;d1=dblcmp(s1=ab_cross_ac(a,b,c),0);d2=dblcmp(s2=ab_cross_ac(a,b,d),0);d3=dblcmp(s3=ab_cross_ac(c,d,a),0);d4=dblcmp(s4=ab_cross_ac(c,d,b),0);//如果规范相交则求交点if ((d1^d2)==-2 && (d3^d4)==-2){p.x=(c.x*s2-d.x*s1)/(s2-s1);p.y=(c.y*s2-d.y*s1)/(s2-s1);return 1;}//如果不规范相交if (d1==0 && point_on_line(c,a,b)<=0){p=c;return 0;}if (d2==0 && point_on_line(d,a,b)<=0){p=d;return 0;}if (d3==0 && point_on_line(a,c,d)<=0){p=a;return 0;}if (d4==0 && point_on_line(b,c,d)<=0){p=b;return 0;}
//如果不相交return -1;
}

Circuit Board

http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=1648
http://www.xuebuyuan.com/552687.html

进阶算法 Bentley-Ottmann 时间复杂度O(n^2)

计算几何-求线段交点算法和代码(C++语言)相关推荐

  1. java 直线交点_[Java教程]谈谈求线段交点的几种算法(js实现,完整版)

    [Java教程]谈谈求线段交点的几种算法(js实现,完整版) 0 2014-08-27 10:05:22 "求线段交点"是一种非常基础的几何计算, 在很多游戏中都会被使用到. 下面 ...

  2. js求两圆交点_谈谈求线段交点的几种算法(js实现,完整版)

    "求线段交点"是一种非常基础的几何计算, 在很多游戏中都会被使用到. 下面我就现学现卖的把最近才学会的一些"求线段交点"的算法总结一下, 希望对大家有所帮助. ...

  3. 扫描线算法-求线段交点数量

    将所有线段的左右端点以X坐标升序放入顶点优先队列中,然后初始化扫描线链表(查找二叉树) 依次取出顶点优先队列中的点, 1 左端点:将其线段插入到扫描线链表中,按照y的升序排列链表,找到在此线段上面相邻 ...

  4. 计算几何_线段交点的快速排斥_跨立实验

    附上题的地址 https://vjudge.net/problem/HDU-1086# // 点是否在矩形 // 矩形点是 st 和 ed bool IsPointInRectangle(Point ...

  5. 贪心算法 背包问题代码 c语言,用贪心算法求解普通背包问题的C++代码

    用贪心算法求解普通背包问题的C++代码 2019年3月6日 125次阅读 来源: 贪心算法 #include #define  M  100 void display(int &n,doubl ...

  6. c语言递归最小值,递归求最大最小值算法 分治策略(c语言实现)

    思路:运用分治的思想,将要排序的整个数组从中间劈开,分别求其左右两边的最大最小值,然后将求出的最大最小值合起来进行比较. 当左右两边的数组小到一定程度时: (1)数组中只有一个元素,maxNum=mi ...

  7. c语言dfs算法全排列代码,c语言dfs解决全排列问题

    如1,2,3三个元素的全排列为: 1,2,3 1,3,2 2,1,3 2,3,1 3,1,2 3,2,1 共3*2*1=6种 代码简单实现n个元素的全排列 #include #define N 5 i ...

  8. c语言求13为质数的代码,C语言求质数.doc

    C语言求质数 试编写一个程序,找出2->N之间的所有质数.希望用尽可能快的方法实现.[问题分析]: 这个问题可以有两种解法:一种是用"筛子法",另一种是从2->N检查, ...

  9. 遗传算法(求函数极值)简易代码C语言200行

    思路: //目标函数:y=(x1-1)^2+(x2-2)^2; #include<stdio.h> #include<time.h> #include<windows.h ...

  10. js求两圆交点_详解js实现线段交点的三种算法

    本文讲的内容都很初级, 主要是面向和我一样的初学者, 所以请各位算法帝们轻拍啊 引用 已知线段1(a,b) 和线段2(c,d) ,其中a b c d为端点, 求线段交点p .(平行或共线视作不相交) ...

最新文章

  1. insert into与insert ignore以及replace into的区别
  2. 【Android FFMPEG 开发】FFMPEG ANativeWindow 原生绘制 ( Java 层获取 Surface | 传递画布到本地 | 创建 ANativeWindow )
  3. Spring MVC-02循序渐进之解耦控制器和校验器
  4. [\u4e00-\u9fa5] //匹配中文字符
  5. svn查看某个时间段的日志信息
  6. vbs比较两个数组里的数的大小_BAT 高频面试题:寻找两个有序数组的中位数
  7. InnoDB存储引擎学习笔记(更新ing)
  8. 2021华为软件精英挑战赛的baseLine,Java版,仅供参考,无核心算法
  9. ios抓包软件Thor限时折扣6元中,手慢无
  10. python数字雨_用Python实现黑客帝国代码雨效果(3种方式)
  11. (ExcelVBA编程入门范例)
  12. 驱动精灵扩展版(集成万能网卡驱动)无法自动识别网卡的解决方案
  13. 锐捷AP组建无线网络-保姆级教程
  14. php泥浆护壁,扩孔泥浆护壁式集束式潜孔锤技术
  15. 有哪些值得长期坚持下去的好习惯?
  16. 尚硅谷SpringBoot学习笔记
  17. 修改变量名,简单有效地提高代码质量!
  18. laraveladmin省市区三级联动
  19. 杭州计算机中级职称评级流程,杭州本地中级工程师****流程
  20. JAVA 爬虫 抖音视频

热门文章

  1. 跨域访问JQuery+.NET实现的一种思路,以及极简单Demo
  2. o'LogonUI Changer XP登陆界面更换器
  3. .net winform 里控件的Dock属性(Dock的Z 顺序停靠)
  4. mali GPU 官网指南
  5. 在java中2l是什么_(二)常量定义
  6. 山东省计算机考试无法报名,12月20日开始报名!山东2020年3月全国计算机等级考试注意事项来咯...
  7. 漫画:什么是桶排序?
  8. Linux内核CPU负载均衡机制
  9. Linux-2.6.32 NUMA架构之内存和调度
  10. Junit框架使用--JUnit常用断言及注解