logo资料库

计算几何资料(c++).doc

第1页 / 共17页
第2页 / 共17页
第3页 / 共17页
第4页 / 共17页
第5页 / 共17页
第6页 / 共17页
第7页 / 共17页
第8页 / 共17页
资料共17页,剩余部分请下载后查看
计算几何资料 一、引言 计算机的出现使得很多原本十分繁琐的工作得以大幅度简化,但是也有一些在人们直观看来很容易的 问题却需要拿出一套并不简单的通用解决方案,比如几何问题。作为计算机科学的一个分支,计算几何主 要研究解决几何问题的算法。在现代工程和数学领域,计算几何在图形学、机器人技术、超大规模集成电 路设计和统计等诸多领域有着十分重要的应用。在本文中,我们将对计算几何常用的基本算法做一个全面 的介绍,希望对您了解并应用计算几何的知识解决问题起到帮助。 二、目录 本文整理的计算几何基本概念和常用算法包括如下内容: 1. 矢量的概念 2. 矢量加减法 3. 矢量叉积 4. 折线段的拐向判断 5. 判断点是否在线段上 6. 判断两线段是否相交 7. 判断线段和直线是否相交 8. 判断矩形是否包含点 9. 判断线段、折线、多边形是否在矩形中 10. 判断矩形是否在矩形中 11. 判断圆是否在矩形中 12. 判断点是否在多边形中 13. 判断线段是否在多边形内 14. 判断折线是否在多边形内 15. 判断多边形是否在多边形内 16. 判断矩形是否在多边形内 17. 判断圆是否在多边形内 18. 判断点是否在圆内 19. 判断线段、折线、矩形、多边形是否在圆内 20. 判断圆是否在圆内 21. 计算点到线段的最近点 22. 计算点到折线、矩形、多边形的最近点 23. 计算点到圆的最近距离及交点坐标 24. 计算两条共线的线段的交点 25. 计算线段或直线与线段的交点 26. 求线段或直线与折线、矩形、多边形的交点 27. 求线段或直线与圆的交点 28. 凸包的概念 29. 凸包的求法 三、算法介绍 1.矢量的概念: 如果一条线段的端点是有次序之分的,我们把这种线段成为有向线段(directed segment)。如果有向线 段 p1p2 的起点 p1 在坐标原点,我们可以把它称为矢量(vector)p2。 2.矢量加减法:
设二维矢量 P = ( x1, y1 ),Q = ( x2 , y2 ),则矢量加法定义为: P + Q = ( x1 + x2 , y1 + y2 ),同 样的,矢量减法定义为: P - Q = ( x1 - x2 , y1 - y2 )。显然有性质 P + Q = Q + P,P - Q = - ( Q - P )。 3. 矢量叉积: 计算矢量叉积是与直线和线段相关算法的核心部分。设矢量 P = ( x1, y1 ),Q = ( x2, y2 ),则矢量叉 积定义为由(0,0)、p1、p2 和 p1+p2 所组成的平行四边形的带符号的面积,即:P × Q = x1*y2 - x2*y1, 其结果是一个标量。显然有性质 P × Q = - ( Q × P ) 和 P × ( - Q ) = - ( P × Q )。一般在不加说明的情 况下,本文下述算法中所有的点都看作矢量,两点的加减法就是矢量相加减,而点的乘法则看作矢量叉积。 叉积的一个非常重要性质是可以通过它的符号判断两矢量相互之间的顺逆时针关系: 若 P × Q > 0 , 则 P 在 Q 的顺时针方向。 若 P × Q < 0 , 则 P 在 Q 的逆时针方向。 若 P × Q = 0 , 则 P 与 Q 共线,但可能同向也可能反向。 4. 折线段的拐向判断: 折线段的拐向判断方法可以直接由矢量叉积的性质推出。对于有公共端点的线段 p0p1 和 p1p2,通过 计算(p2 - p0) × (p1 - p0)的符号便可以确定折线段的拐向: 若(p2 - p0) × (p1 - p0) > 0,则 p0p1 在 p1 点拐向右侧后得到 p1p2。 若(p2 - p0) × (p1 - p0) < 0,则 p0p1 在 p1 点拐向左侧后得到 p1p2。 若(p2 - p0) × (p1 - p0) = 0,则 p0、p1、p2 三点共线。 具体情况可参考下图: 5. 判断点是否在线段上: 设点为 Q,线段为 P1P2 ,判断点 Q 在该线段上的依据是:( Q - P1 ) × ( P2 - P1 ) = 0 且 Q 在以 P1, P2 为对角顶点的矩形内。前者保证 Q 点在直线 P1P2 上,后者是保证 Q 点不在线段 P1P2 的延长线或反向 延长线上,对于这一步骤的判断可以用以下过程实现: ON-SEGMENT(pi,pj,pk) if min(xi,xj) <= xk <= max(xi,xj) and min(yi,yj) <= yk <= max(yi,yj) then return true; else return false; 特别要注意的是,由于需要考虑水平线段和垂直线段两种特殊情况,min(xi,xj)<=xk<=max(xi,xj)和 min(yi,yj)<=yk<=max(yi,yj)两个条件必须同时满足才能返回真值。 6. 判断两线段是否相交: 我们分两步确定两条线段是否相交: (1)快速排斥试验 设以线段 P1P2 为对角线的矩形为 R, 设以线段 Q1Q2 为对角线的矩形为 T,如果 R 和 T 不相 交,显然两线段不会相交。 (2)跨立试验 如果两线段相交,则两线段必然相互跨立对方。若 P1P2 跨立 Q1Q2 ,则矢量 ( P1 - Q1 ) 和( P2 - Q1 )位于矢量( Q2 - Q1 ) 的两侧,即( P1 - Q1 ) × ( Q2 - Q1 ) * ( P2 - Q1 ) × ( Q2 - Q1 ) < 0。上式可 改写成( P1 - Q1 ) × ( Q2 - Q1 ) * ( Q2 - Q1 ) × ( P2 - Q1 ) > 0。当 ( P1 - Q1 ) × ( Q2 - Q1 ) = 0 时, 说明 ( P1 - Q1 ) 和 ( Q2 - Q1 )共线,但是因为已经通过快速排斥试验,所以 P1 一定在线段 Q1Q2 上; 同理,( Q2 - Q1 ) ×(P2 - Q1 ) = 0 说明 P2 一定在线段 Q1Q2 上。所以判断 P1P2 跨立 Q1Q2 的依据是: ( P1 - Q1 ) × ( Q2 - Q1 ) * ( Q2 - Q1 ) × ( P2 - Q1 ) >= 0。同理判断 Q1Q2 跨立 P1P2 的依据是:( Q1 - P1 ) × ( P2 - P1 ) * ( P2 - P1 ) × ( Q2 - P1 ) >= 0。具体情况如下图所示: 在相同的原理下,对此算法的具体的实现细节可能会与此有所不同,除了这种过程外,大家也可以参 考《算法导论》上的实现。 7. 判断线段和直线是否相交:
有了上面的基础,这个算法就很容易了。如果线段 P1P2 和直线 Q1Q2 相交,则 P1P2 跨立 Q1Q2,即: ( P1 - Q1 ) × ( Q2 - Q1 ) * ( Q2 - Q1 ) × ( P2 - Q1 ) >= 0。 8. 判断矩形是否包含点: 只要判断该点的横坐标和纵坐标是否夹在矩形的左右边和上下边之间。 9. 判断线段、折线、多边形是否在矩形中: 因为矩形是个凸集,所以只要判断所有端点是否都在矩形中就可以了。 10. 11. 判断矩形是否在矩形中: 只要比较左右边界和上下边界就可以了。 判断圆是否在矩形中: 很容易证明,圆在矩形中的充要条件是:圆心在矩形中且圆的半径小于等于圆心到矩形四边的距离的 最小值。 12. 判断点是否在多边形中: 判断点 P 是否在多边形中是计算几何中一个非常基本但是十分重要的算法。以点 P 为端点,向左方作 射线 L,由于多边形是有界的,所以射线 L 的左端一定在多边形外,考虑沿着 L 从无穷远处开始自左向右 移动,遇到和多边形的第一个交点的时候,进入到了多边形的内部,遇到第二个交点的时候,离开了多边 形,……所以很容易看出当 L 和多边形的交点数目 C 是奇数的时候,P 在多边形内,是偶数的话 P 在多边形 外。 但是有些特殊情况要加以考虑。如图下图(a)(b)(c)(d)所示。在图(a)中,L 和多边形的顶点相交,这时 候交点只能计算一个;在图(b)中,L 和多边形顶点的交点不应被计算;在图(c)和(d) 中,L 和多边形的一 条边重合,这条边应该被忽略不计。如果 L 和多边形的一条边重合,这条边应该被忽略不计。 为了统一起见,我们在计算射线 L 和多边形的交点的时候,1。对于多边形的水平边不作考虑;2。对 于多边形的顶点和 L 相交的情况,如果该顶点是其所属的边上纵坐标较大的顶点,则计数,否则忽略;3。 对于 P 在多边形边上的情形,直接可判断 P 属于多边行。由此得出算法的伪代码如下: count ← 0; 以 P 为端点,作从右向左的射线 L; for 多边形的每条边 s do if P 在边 s 上 then return true; if s 不是水平的 then if s 的一个端点在 L 上 if 该端点是 s 两端点中纵坐标较大的端点 then count ← count+1 else if s 和 L 相交 then count ← count+1; if count mod 2 = 1 then return true; else return false; 其中做射线 L 的方法是:设 P'的纵坐标和 P 相同,横坐标为正无穷大(很大的一个正数),则 P 和 P' 就确定了射线 L。 判断点是否在多边形中的这个算法的时间复杂度为 O(n)。 另外还有一种算法是用带符号的三角形面积之和与多边形面积进行比较,这种算法由于使用浮点数运 算所以会带来一定误差,不推荐大家使用。 13. 判断线段是否在多边形内: 线段在多边形内的一个必要条件是线段的两个端点都在多边形内,但由于多边形可能为凹,所以这不
能成为判断的充分条件。如果线段和多边形的某条边内交(两线段内交是指两线段相交且交点不在两线段 的端点),因为多边形的边的左右两侧分属多边形内外不同部分,所以线段一定会有一部分在多边形外(见 图 a)。于是我们得到线段在多边形内的第二个必要条件:线段和多边形的所有边都不内交。 线段和多边形交于线段的两端点并不会影响线段是否在多边形内;但是如果多边形的某个顶点和线段 相交,还必须判断两相邻交点之间的线段是否包含于多边形内部(反例见图 b)。 因此我们可以先求出所有和线段相交的多边形的顶点,然后按照 X-Y 坐标排序(X 坐标小的排在前面, 对于 X 坐标相同的点,Y 坐标小的排在前面,这种排序准则也是为了保证水平和垂直情况的判断正确),这 样相邻的两个点就是在线段上相邻的两交点,如果任意相邻两点的中点也在多边形内,则该线段一定在多 边形内。 证明如下: 命题 1: 如果线段和多边形的两相邻交点 P1 ,P2 的中点 P' 也在多边形内,则 P1, P2 之间的所有点都在 多边形内。 证明: 假设 P1,P2 之间含有不在多边形内的点,不妨设该点为 Q,在 P1, P'之间,因为多边形是闭合曲 线,所以其内外部之间有界,而 P1 属于多边行内部,Q 属于多边性外部,P'属于多边性内部,P1-Q-P'完全 连续,所以 P1Q 和 QP'一定跨越多边形的边界,因此在 P1,P'之间至少还有两个该线段和多边形的交点,这 和 P1P2 是相邻两交点矛盾,故命题成立。证毕。 由命题 1 直接可得出推论: 推论 2: 设多边形和线段 PQ 的交点依次为 P1,P2,……Pn,其中 Pi 和 Pi+1 是相邻两交点,线段 PQ 在多边形内 的充要条件是:P,Q 在多边形内且对于 i =1, 2,……, n-1,Pi ,Pi+1 的中点也在多边形内。 在实际编程中,没有必要计算所有的交点,首先应判断线段和多边形的边是否内交,倘若线段和多边 形的某条边内交则线段一定在多边形外;如果线段和多边形的每一条边都不内交,则线段和多边形的交点 一定是线段的端点或者多边形的顶点,只要判断点是否在线段上就可以了。 至此我们得出算法如下: if 线端 PQ 的端点不都在多边形内 then return false; 点集 pointSet 初始化为空; for 多边形的每条边 s do if 线段的某个端点在 s 上 then 将该端点加入 pointSet; else if s 的某个端点在线段 PQ 上 then 将该端点加入 pointSet; else if s 和线段 PQ 相交 // 这时候已经可以肯定是内交了 then return false; 将 pointSet 中的点按照 X-Y 坐标排序; for pointSet 中每两个相邻点 pointSet[ i ] , pointSet[ i+1] do if pointSet [ i] , pointSet[ i+1] 的中点不在多边形中 then return false; return true; 这个过程中的排序因为交点数目肯定远小于多边形的顶点数目 n,所以最多是常数级的复杂度,几乎 可以忽略不计。因此算法的时间复杂度也是 O(n)。
14. 判断折线是否在多边形内: 只要判断折线的每条线段是否都在多边形内即可。设折线有 m 条线段,多边形有 n 个顶点,则该算法 的时间复杂度为 O(m*n)。 15. 判断多边形是否在多边形内: 只要判断多边形的每条边是否都在多边形内即可。判断一个有 m 个顶点的多边形是否在一个有 n 个顶 点的多边形内复杂度为 O(m*n)。 16. 判断矩形是否在多边形内: 将矩形转化为多边形,然后再判断是否在多边形内。 17 判断圆是否在多边形内: 只要计算圆心到多边形的每条边的最短距离,如果该距离大于等于圆半径则该圆在多边形内。计算圆 心到多边形每条边最短距离的算法在后文阐述。 18. 判断点是否在圆内: 计算圆心到该点的距离,如果小于等于半径则该点在圆内。 判断线段、折线、矩形、多边形是否在圆内: 因为圆是凸集,所以只要判断是否每个顶点都在圆内即可。 判断圆是否在圆内: 19. 20. 设两圆为 O1,O2,半径分别为 r1, r2,要判断 O2 是否在 O1 内。先比较 r1,r2 的大小,如果 r1
25. 计算线段或直线与线段的交点: 设一条线段为 L0 = P1P2,另一条线段或直线为 L1 = Q1Q2 ,要计算的就是 L0 和 L1 的交点。 1. 首先判断 L0 和 L1 是否相交(方法已在前文讨论过),如果不相交则没有交点,否则说明 L0 和 L1 一定有交点,下面就将 L0 和 L1 都看作直线来考虑。 2. 如果 P1 和 P2 横坐标相同,即 L0 平行于 Y 轴 a) 若 L1 也平行于 Y 轴, i. 若 P1 的纵坐标和 Q1 的纵坐标相同,说明 L0 和 L1 共线,假如 L1 是直线的话他们有无穷的交 点,假如 L1 是线段的话可用"计算两条共线线段的交点"的算法求他们的交点(该方法在前文已讨论过); ii. 否则说明 L0 和 L1 平行,他们没有交点; b) 若 L1 不平行于 Y 轴,则交点横坐标为 P1 的横坐标,代入到 L1 的直线方程中可以计算出交点纵坐 标; 3. 如果 P1 和 P2 横坐标不同,但是 Q1 和 Q2 横坐标相同,即 L1 平行于 Y 轴,则交点横坐标为 Q1 的 横坐标,代入到 L0 的直线方程中可以计算出交点纵坐标; 4. 如果 P1 和 P2 纵坐标相同,即 L0 平行于 X 轴 a) 若 L1 也平行于 X 轴, i. 若 P1 的横坐标和 Q1 的横坐标相同,说明 L0 和 L1 共线,假如 L1 是直线的话他们有无穷的交 点,假如 L1 是线段的话可用"计算两条共线线段的交点"的算法求他们的交点(该方法在前文已讨论过); ii. 否则说明 L0 和 L1 平行,他们没有交点; b) 若 L1 不平行于 X 轴,则交点纵坐标为 P1 的纵坐标,代入到 L1 的直线方程中可以计算出交点横坐 标; 5. 如果 P1 和 P2 纵坐标不同,但是 Q1 和 Q2 纵坐标相同,即 L1 平行于 X 轴,则交点纵坐标为 Q1 的 纵坐标,代入到 L0 的直线方程中可以计算出交点横坐标; 6. 剩下的情况就是 L1 和 L0 的斜率均存在且不为 0 的情况 a) 计算出 L0 的斜率 K0,L1 的斜率 K1 ; b) 如果 K1 = K2 i. 如果 Q1 在 L0 上,则说明 L0 和 L1 共线,假如 L1 是直线的话有无穷交点,假如 L1 是线段的话 可用"计算两条共线线段的交点"的算法求他们的交点(该方法在前文已讨论过); ii. 如果 Q1 不在 L0 上,则说明 L0 和 L1 平行,他们没有交点。 c) 联立两直线的方程组可以解出交点来 这个算法并不复杂,但是要分情况讨论清楚,尤其是当两条线段共线的情况需要单独考虑,所以在前 文将求两条共线线段的算法单独写出来。另外,一开始就先利用矢量叉乘判断线段与线段(或直线)是否 相交,如果结果是相交,那么在后面就可以将线段全部看作直线来考虑。需要注意的是,我们可以将直线 或线段方程改写为 ax+by+c=0 的形式,这样一来上述过程的部分步骤可以合并,缩短了代码长度,但是 由于先要求出参数,这种算法将花费更多的时间。 26. 求线段或直线与折线、矩形、多边形的交点: 分别求与每条边的交点即可。 27. 求线段或直线与圆的交点: 设圆心为 O,圆半径为 r,直线(或线段)L 上的两点为 P1,P2。 1. 如果 L 是线段且 P1,P2 都包含在圆 O 内,则没有交点;否则进行下一步。 2. 如果 L 平行于 Y 轴, a) 计算圆心到 L 的距离 dis; b) 如果 dis > r 则 L 和圆没有交点; c) 利用勾股定理,可以求出两交点坐标,但要注意考虑 L 和圆的相切情况。 3. 如果 L 平行于 X 轴,做法与 L 平行于 Y 轴的情况类似;
4. 如果 L 既不平行 X 轴也不平行 Y 轴,可以求出 L 的斜率 K,然后列出 L 的点斜式方程,和圆方程联 立即可求解出 L 和圆的两个交点; 5. 如果 L 是线段,对于 2,3,4 中求出的交点还要分别判断是否属于该线段的范围内。 28. 凸包的概念: 点集 Q 的凸包(convex hull)是指一个最小凸多边形,满足 Q 中的点或者在多边形边上或者在其内。下 图中由红色线段表示的多边形就是点集 Q={p0,p1,...p12}的凸包。 29. 凸包的求法: 现在已经证明了凸包算法的时间复杂度下界是 O(n*logn),但是当凸包的顶点数 h 也被考虑进去的话, Krikpatrick 和 Seidel 的剪枝搜索算法可以达到 O(n*logh),在渐进意义下达到最优。最常用的凸包算法是 Graham 扫描法和 Jarvis 步进法。本文只简单介绍一下 Graham 扫描法,其正确性的证明和 Jarvis 步进法的 过程大家可以参考《算法导论》。 对于一个有三个或以上点的点集 Q,Graham 扫描法的过程如下: 令 p0 为 Q 中 Y-X 坐标排序下最小的点 设为对其余点按以 p0 为中心的极角逆时针排序所得的点集(如果有多个点有相同的极 角,除了距 p0 最远的点外全部移除 压 p0 进栈 S 压 p1 进栈 S 压 p2 进栈 S for i ← 3 to m do while 由 S 的栈顶元素的下一个元素、S 的栈顶元素以及 pi 构成的折线段不拐向左侧 对 S 弹栈 压 pi 进栈 S return S; 此过程执行后,栈 S 由底至顶的元素就是 Q 的凸包顶点按逆时针排列的点序列。需要注意的是,我们 对点按极角逆时针排序时,并不需要真正求出极角,只需要求出任意两点的次序就可以了。而这个步骤可 以用前述的矢量叉积性质实现。 四、结语 尽管人类对几何学的研究从古代起便没有中断过,但是具体到借助计算机来解决几何问题的研究,还 只是停留在一个初级阶段,无论从应用领域还是发展前景来看,计算几何学都值得我们认真学习、加以运 用,希望这篇文章能带你走进这个丰富多彩的世界。 目录 ㈠ 点的基本运算 1. 平面上两点之间距离 1 2. 判断两点是否重合 1 3. 矢量叉乘 1 4. 矢量点乘 2 5. 判断点是否在线段上 2 6. 求一点饶某点旋转后的坐标 2 7. 求矢量夹角 2 ㈡ 线段及直线的基本运算 1. 点与线段的关系 3 2. 求点到线段所在直线垂线的垂足 4 3. 点到线段的最近点 4
4. 点到线段所在直线的距离 4 5. 点到折线集的最近距离 4 6. 判断圆是否在多边形内 5 7. 求矢量夹角余弦 5 8. 求线段之间的夹角 5 9. 判断线段是否相交 6 10.判断线段是否相交但不交在端点处 6 11.求线段所在直线的方程 6 12.求直线的斜率 7 13.求直线的倾斜角 7 14.求点关于某直线的对称点 7 15.判断两条直线是否相交及求直线交点 7 16.判断线段是否相交,如果相交返回交点 7 ㈢ 多边形常用算法模块 1. 判断多边形是否简单多边形 8 2. 检查多边形顶点的凸凹性 9 3. 判断多边形是否凸多边形 9 4. 求多边形面积 9 5. 判断多边形顶点的排列方向,方法一 10 6. 判断多边形顶点的排列方向,方法二 10 7. 射线法判断点是否在多边形内 10 8. 判断点是否在凸多边形内 11 9. 寻找点集的 graham 算法 12 10.寻找点集凸包的卷包裹法 13 11.判断线段是否在多边形内 14 12.求简单多边形的重心 15 13.求凸多边形的重心 17 14.求肯定在给定多边形内的一个点 17 15.求从多边形外一点出发到该多边形的切线 18 16.判断多边形的核是否存在 19 ㈣ 圆的基本运算 1 .点是否在圆内 20 2 .求不共线的三点所确定的圆 21 ㈤ 矩形的基本运算 1.已知矩形三点坐标,求第 4 点坐标 22 ㈥ 常用算法的描述 22 ㈦ 补充 1.两圆关系: 24 2.判断圆是否在矩形内: 24
分享到:
收藏