我在上一篇博客中(详见:计算几何问题汇总–点与线的位置关系)谈到了计算几何最基本的问题:解决点与线(线段or直线)的位置关系判断。那么,更进一步,还需要探讨复杂一点的情况:比如面与线,面与面之间的关系。本文中,我就先说说最常见的两种几何图形:圆与矩形。我将就矩形与圆的碰撞判断问题、线与矩形、线与圆之间的碰撞问题作出分析,以及给出这些解决问题的算法、代码。

在此之前,我默认所有对本文内容感兴趣的读者,都了解基本的计算几何概念,并且对我的上一篇博客:计算几何问题汇总–点与线的位置关系 所讲的内容基本熟悉。因为本文涉及的算法,几乎全部都用到了上一篇博客中所定义的类和函数。

简单将上一篇博客的内容陈列如下,方便读者阅读后面本文的算法:

模块名:PointLine

class Point: 点类,两个属性,分别是横纵坐标x, yclass Segment: 线段类,两个属性,分别是两个端点p1, p2class Line: 直线类,两个属性,分别是直线上任意两点p1, p2class Vector: 向量类,以向量的起点和终点为参数,建立一个由横纵坐标为两个属性的类func: pointDistance(p1, p2): 计算两点之间距离的函数。返回一个浮点型的值func: pointToLine(C, AB): 计算点C到直线AB的距离。返回一个浮点型的值func: pointInLine(C, AB): 判断点C是否在直线AB上。在,返回True;不在,返回Falsefunc: pointInSegment(C, AB): 判断点C是否在线段AB上。在,返回True;不在,返回Falsefunc: linesAreParallel(l1, l2): 判断两条直线l1, l2是否平行。平行,返回True;不平行,返回Falsefunc: crossProduct(v1, v2): 计算两个向量叉积func: segmentsIntersect(s1, s2): 判断两个线段是否相交func: segmentIntersectsLine(segment, line): 判断一条线段和一条直线是否相交

好了,以上是我在上一篇博客中定义的类和函数,我把他们的基本功能,参数设置等等简要列在了上面,以便理解本文后面将要给出的代码。至于具体的算法及分析,请翻看我的上一篇博客。

需要注意的是,我在本文中,把上一篇博客的工作全部封装成了一个模块:PointLine.py,方便本文的代码调用。

接下来,就进入本文的主题吧。

1. 圆与点的位置关系

简单来分,二维空间内,圆与点的位置关系,只有两种:

(1)点在圆上(包括在圆周上以及在圆内);
(2)点在圆外

而判断的方法很直接,计算点到圆心的距离,再用这个距离和圆的半径比较即可。

先定义一个圆类:两个属性,圆心及半径。

class Circle(object):"""Circle has 2 attributes: center and radius"""def __init__(self, center, radius):self.center = centerself.radius = radius

然后写出判断点与圆关系的完整代码如下:

from PointLine import *class Circle(object):"""Circle has 2 attributes: center and radius"""def __init__(self, center, radius):self.center = centerself.radius = radiusdef pointInCircle(point, circle):"""determine whether a point in a circle"""return (pointDistance(point, circle.center) - circle.radius) < 1e-9

其中,pointDistance() 函数在模块 PointLine 已经存在。

最后的距离判断还是要满足浮点值比较的原则,上一篇博客中详细说过,不再赘述。

2. 圆与线的位置关系

(1) 圆与直线的位置关系

简单划分,还是两种:相交(包括相切);不相交

判断的依据是根据圆心到直线的距离与圆半径相比较。判别函数如下:

def lineIntersectsCircle(line, circle):"""determine whether a line intersects a circle"""dis = pointToLine(circle.center, line)return dis - circle.radius < 1e-9

其中,pointToLine() 在模块PointLine中已经存在。

(2) 圆与线段的位置关系

线段由于其位置上的限制,导致我们在判断线段和圆的关系的时候会麻烦一点。判断思路如下:

  1. 判断线段ss所在的直线ll与圆心的距离disdis和圆的半径rr之间的关系。若dis<=rdis 则进行2、3步的判断;若dis>rdis > r ,直接返回False.(也就是说肯定与圆不相交)
  2. 计算出圆心到直线ll的垂线与直线ll的交点(也就是垂足),记为点DD. 此处的计算方法参考我在上一篇博客中计算点到直线距离时,所使用的向量法。(详见:计算几何问题汇总–点与线的位置关系)
  3. 判断点DD是否在线段ss上,判断方法是 PointLine 模块中的函数PointInSegment()。 这里直接调用就行。

代码如下:

def segmentIntersectsCircle(AB, circle):"""determine whether a segment intersects a circle"""if not lineIntersectsCircle(Line(AB.p1, AB.p2), circle):return Falseif pointInCircle(AB.p1, circle) or pointInCircle(AB.p2, circle):return Truevector_AB = Vector(AB.p1, AB.p2)vector_AO = Vector(AB.p1, circle.center)# two ndarray objecttAB = np.array([vector_AB.x, vector_AB.y])tAO = np.array([vector_AO.x, vector_AO.y])# vector AD, type: ndarraytAD = ((tAB @ tAO) / (tAB @ tAB)) * tAB# get point DDx, Dy = tAD[0] + AB.p1.x, tAD[1] + AB.p1.yD = Point(Dx, Dy)return pointInCircle(D, circle) and pointInSegment(D, AB)

运行此函数的前提是还是导入模块PointLine以及库numpy

把上面的所有函数写在一起,方便有需要的读者使用:

from PointLine import *class Circle(object):"""Circle has 2 attributes: center and radius"""def __init__(self, center, radius):self.center = centerself.radius = radiusdef pointInCircle(point, circle):"""determine whether a point in a circle"""return (pointDistance(point, circle.center) - circle.radius) < 1e-9def lineIntersectsCircle(line, circle):"""determine whether a line intersects a circle"""dis = pointToLine(circle.center, line)return dis - circle.radius < 1e-9def segmentIntersectsCircle(AB, circle):"""determine whether a segment intersects a circle"""if not lineIntersectsCircle(Line(AB.p1, AB.p2), circle):return Falseif pointInCircle(AB.p1, circle) or pointInCircle(AB.p2, circle):return Truevector_AB = Vector(AB.p1, AB.p2)vector_AO = Vector(AB.p1, circle.center)# two ndarray objecttAB = np.array([vector_AB.x, vector_AB.y])tAO = np.array([vector_AO.x, vector_AO.y])# vector AD, type: ndarraytAD = ((tAB @ tAO) / (tAB @ tAB)) * tAB# get point DDx, Dy = tAD[0] + AB.p1.x, tAD[1] + AB.p1.yD = Point(Dx, Dy)return pointInCircle(D, circle) and pointInSegment(D, AB)

这就可以构成一个关于圆的相关问题的计算模块了。其中,引入的模块 PointLine 详见我的上一篇博客,链接前面已经给出了。

矩形

了解了圆的问题之后,我们看看矩形。这里,我将圆与矩形的位置关系,也放在矩形中讲解。

在二维平面空间中,若要定义矩形,则至少需要知道矩形的三个顶点,如Fig.1(a)所示。若是有矩形的边一定和坐标轴平行或垂直的条件,则只需要知道两个顶点就行,如Fig.1(b)所示。为使算法具有普遍性,我们研究Fig.1(a)所示的情况:

先给出矩形类:

class Rectangle(object):"""four points are defined by clockwise order from upper left corner"""def __init__(self, p1, p2, p3, p4):self.p1, self.p2, self.p3, self.p4 = p1, p2, p3, p4def getCenter(self):return Point((self.p2.x + self.p4.x) / 2, (self.p2.y + self.p4.y) / 2)def getXline(self):rectangle_center = self.getCenter()x_center = Point((self.p2.x + self.p3.x) / 2, (self.p2.y + self.p3.y) / 2)return Line(x_center, rectangle_center)def getYline(self):rectangle_center = self.getCenter()y_center = Point((self.p1.x + self.p2.x) / 2, (self.p1.y + self.p2.y) / 2)return Line(y_center, rectangle_center)

构造函数中,用矩形的四个顶点构造了一个矩形对象,其实,三个顶点就够了,但在这里,4个还是3个影响不大。

此外,除了初始化对象的构造函数,我还给出了三个矩形类的方法:

getCenter:用来返回矩形的中心点,也就是Fig.2(a)中的点OO;
getXline:用来返回经过矩形的中心点OO,且平行于矩形一边的轴线,作为针对某个矩形的新的XX轴。如Fig.2(a)所示;
getYline:与矩形新的XX轴的构造同理,构造一个针对某个矩形的YY轴

显而易见,如果对一个矩形对象执行以上三种方法,我们就可以得到一个以矩形中心点为坐标原点,分别平行于矩形的高和宽的新的坐标系。

1. 点和矩形的位置关系

明确了这一点,再看问题。首先解决点是否在矩形中的问题(这里说的在矩形中,包括在矩形边线上的情况):

想要一个点,比如Fig.2(a)中的点PP,在矩形中,那么这个点与上面所说的新建的坐标系一定要同时满足以下两个条件:

  1. 点PP到新的XX轴的距离小于等于矩形高度的1/21/2
  2. 点PP到新的YY轴的距离小于等于矩形宽度的1/21/2

由此,可以写出代码:

from PointLine import *def pointInRectangle(point, rectangle):"""determine whether a point in a rectangle"""x_line = rectangle.getXline()y_line = rectangle.getYline()d1 = pointToLine(point, y_line) - pointToLine(rectangle.p2, y_line)d2 = pointToLine(point, x_line) - pointToLine(rectangle.p2, x_line)return d1 < 1e-9 and d2 < 1e-9

函数 PointToLine() 在模块PointLine中给出了。

2. 线和矩形的位置关系

借助点和矩形位置关系的判断方法,我们可以判断线段和矩形的位置关系。若一条线段和矩形相交(包括线段和矩形的边相交以及线段在矩形内的情况),那么线段必须满足以下两个条件之一:

  1. 线段的两个端点全部在矩形内
  2. 线段和至少一条矩形的边相交

如图Fig.2(b)所示,线段和矩形相交的情况都可以被以上的两个条件涵盖。因此,根据这个思路写出代码即可。

def segmentIntersectsRectangle(s, rectangle):"""determine whether a segment intersects a rectangle"""s1 = Segment(rectangle.p1, rectangle.p2)s2 = Segment(rectangle.p2, rectangle.p3)s3 = Segment(rectangle.p3, rectangle.p4)s4 = Segment(rectangle.p4, rectangle.p1)segmentsList = [s1, s2, s3, s4]if pointInRectangle(s.p1, rectangle) and pointInRectangle(s.p2, rectangle):return Truefor segment in segmentsList:if segmentsIntersect(segment, s):return Truereturn False

s1−s4s_1 - s_4是矩形的四条边,都是线段。

同理,可以写出直线与矩形位置关系的判别函数。与线段不同的是,直线与矩形任意边相交
是直线与矩形相交的充要条件。给出代码,如下:

def lineIntersectsRectangle(l, rectangle):"""determine whether a line intersects a rectangle"""s1 = Segment(rectangle.p1, rectangle.p2)s2 = Segment(rectangle.p2, rectangle.p3)s3 = Segment(rectangle.p3, rectangle.p4)s4 = Segment(rectangle.p4, rectangle.p1)segmentsList = [s1, s2, s3, s4]for segment in segmentsList:if segmentIntersectsLine(segment, l):return Truereturn False

3. 圆与矩形的碰撞检测

现在,已经解决了圆和矩形的基本计算问题,那么如何确定圆跟矩形的位置关系呢,换句话说,如何设计一个检测圆与矩形是否碰撞的算法?

和判断线与矩形,点与矩形的办法一样,还是需要先根据矩形,建立新的坐标系,如图Fig.3所示,然后计算圆心到这个新坐标系XX轴和YY轴的距离,根据这个距离与圆的半径、矩形边长的关系可以检测碰撞。

先想一想圆和矩形碰撞的碰撞(相交)的情况,其实一共就三种:

  1. 圆只与矩形的一条边相交,并不和矩形的顶点相交。如图Fig.3(a)所示
  2. 圆与矩形的一个或多个顶点相交,也包含了矩形完全在圆内的情况。如图Fig.3(b)所示
  3. 圆在矩形内。如图Fig.3(c)所示


现在,设dxd_x为圆心到新坐标系XX轴的距离,而dyd_y为圆心到新坐标系YY轴的距离,ww为矩形的宽度,hh为矩形的高度。就像Fig.3中所标出的那样。

可以按如下思路讨论:

  1. 判断矩形的四个顶点是否在圆中,只要有一个在,那么矩形与圆碰撞。也就是上面说的情况2;
  2. 判断dx<=h2d_x 且 dy<=r+w2d_y 是否成立,若成立,则圆一定与矩形的一条高相交,如图Fig.4(a)所示;
    • 判断dy<=w2d_y 且 dy<=r+h2d_y 是否成立,若成立,则圆一定与矩形的一条宽相交,如图Fig.4(b)所示;
    • 最后需要说明的是,对于圆在矩形内的情况,2、3步就可以判断了,无需再写代码;
    • 代码如下:

      def circleIntersectsRectangle(circle, rectangle):"""determine whether a circle intersects a rectangle"""if pointInCircle(rectangle.p1, circle) or pointInCircle(rectangle.p1, circle) or\pointInCircle(rectangle.p1, circle) or pointInCircle(rectangle.p1, circle):return Truex_line = rectangle.getXline()y_line = rectangle.getYline()dx, dy = pointToLine(circle.center, x_line), pointToLine(circle.center, y_line)h, w = pointDistance(rectangle.p1, rectangle.p2), pointDistance(rectangle.p2, rectangle.p3)if dx - h / 2 < 1e-9 and dy - (circle.radius + w / 2) < 1e-9:return Trueif dy - w / 2 < 1e-9 and dx - (circle.radius + h / 2) < 1e-9:return Truereturn False

      关于矩形的相关计算的完整代码我放在这里,方便参考:

      
      from Circle import *
      from PointLine import *class Rectangle(object):"""four points are defined by clockwise order from upper left corner"""def __init__(self, p1, p2, p3, p4):self.p1, self.p2, self.p3, self.p4 = p1, p2, p3, p4def getCenter(self):return Point((self.p2.x + self.p4.x) / 2, (self.p2.y + self.p4.y) / 2)def getXline(self):rectangle_center = self.getCenter()x_center = Point((self.p2.x + self.p3.x) / 2, (self.p2.y + self.p3.y) / 2)return Line(x_center, rectangle_center)def getYline(self):rectangle_center = self.getCenter()y_center = Point((self.p1.x + self.p2.x) / 2, (self.p1.y + self.p2.y) / 2)return Line(y_center, rectangle_center)def pointInRectangle(point, rectangle):"""determine whether a point in a rectangle"""x_line = rectangle.getXline()y_line = rectangle.getYline()d1 = pointToLine(point, y_line) - pointToLine(rectangle.p2, y_line)d2 = pointToLine(point, x_line) - pointToLine(rectangle.p2, x_line)return d1 < 1e-9 and d2 < 1e-9def segmentIntersectsRectangle(s, rectangle):"""determine whether a segment intersects a rectangle"""s1 = Segment(rectangle.p1, rectangle.p2)s2 = Segment(rectangle.p2, rectangle.p3)s3 = Segment(rectangle.p3, rectangle.p4)s4 = Segment(rectangle.p4, rectangle.p1)segmentsList = [s1, s2, s3, s4]if pointInRectangle(s.p1, rectangle) and pointInRectangle(s.p2, rectangle):return Truefor segment in segmentsList:if segmentsIntersect(segment, s):return Truereturn Falsedef lineIntersectsRectangle(l, rectangle):"""determine whether a line intersects a rectangle"""s1 = Segment(rectangle.p1, rectangle.p2)s2 = Segment(rectangle.p2, rectangle.p3)s3 = Segment(rectangle.p3, rectangle.p4)s4 = Segment(rectangle.p4, rectangle.p1)segmentsList = [s1, s2, s3, s4]for segment in segmentsList:if segmentIntersectsLine(segment, l):return Truereturn Falsedef circleIntersectsRectangle(circle, rectangle):"""determine whether a circle intersects a rectangle"""if pointInCircle(rectangle.p1, circle) or pointInCircle(rectangle.p1, circle) or\pointInCircle(rectangle.p1, circle) or pointInCircle(rectangle.p1, circle):return Truex_line = rectangle.getXline()y_line = rectangle.getYline()dx, dy = pointToLine(circle.center, x_line), pointToLine(circle.center, y_line)h, w = pointDistance(rectangle.p1, rectangle.p2), pointDistance(rectangle.p2, rectangle.p3)if dx - h / 2 < 1e-9 and dy - (circle.radius + w / 2) < 1e-9:return Trueif dy - w / 2 < 1e-9 and dx - (circle.radius + h / 2) < 1e-9:return Truereturn False
      

      有关于计算几何问题的完整项目我放在了github主页上,并持续更新:Computational-Geometry. 欢迎访问。

计算几何问题汇总--圆与矩形相关推荐

  1. 使用Python,OpenCV进行涂鸦(绘制文字、线、圆、矩形、椭圆、多边形轮廓、多边形填充、箭头~)

    使用Python,OpenCV进行涂鸦(绘制文字.线.圆.矩形.椭圆.多边形轮廓.多边形填充.箭头) 1. 效果图 2. 原理 2.1 绘制线:cv2.line(canvas, (300, 0), ( ...

  2. ArcGIS.Server.9.2.DotNet在ElementGraphicsLayer画点、线、折线、面、圆、矩形的代码

    ArcGIS.Server.9.2.DotNet在ElementGraphicsLayer画点.线.折线.面.圆.矩形的代码:   1public class AddTool:IMapServerTo ...

  3. OpenCV-绘制圆端矩形(药丸)

    作者:Steven 版权声明:著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处 功能函数 // 绘制圆端矩形(药丸状,pill) void DrawPill(cv::Mat mask ...

  4. OpenCV-基本图形绘制(圆、矩形、椭圆)

    作者:翟天保Steven 版权声明:著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处 circle函数原型 void circle(InputOutputArray img, Poi ...

  5. pygame一步步玩转圆、矩形、图像、旋转、透明度alpha、colorkey操作(有完整案例)

    最近一直在专研pygame,略有心得,也分享了几篇文章,感谢各位朋友支持. 今天,玩转一个非常基础的知识,其实是多个知识点的汇聚,欢迎往下看. 文章目录 一.包含的基础知识点: (一)画圆 (二)画矩 ...

  6. cesium动态绘制圆,矩形,自定义区域

    cesium动态绘制圆,矩形,自定义区域 自己封装了一个类,可以放在js文件中引入项目,具体使用和代码如下 class Draw {constructor(viewer, config) {/**ce ...

  7. 算法——判断圆和矩形是否有重叠

    圆和矩形的重叠问题 一.问题描述 二.分析思路 三.代码示例(javascript) 一.问题描述 给你一个以 (radius, x_center, y_center) 表示的圆和一个与坐标轴平行的矩 ...

  8. java程序运行图形_java编程 要用到继承的方法图形计算器项目: 实现一个图形计算器,程序运行后显示界面:请选择图形: 1 圆 2 矩形...

    共回答了12个问题采纳率:91.7% 代码如下.输入参数和计算结果都为整数.具体类型请自行修改.import java.util.Scanner; public class Test { public ...

  9. 【Halcon二维测量】——使用计量模型以亚像素精度测量圆和矩形

    算法大致思路如下:       1.创建计量模型模板并设置计量对象图像大小       2.根据先验知识生成相关图形形状,如矩形,圆,并将其加入到创建的模板中       3.设置模板的相关参数,包括 ...

  10. C#图像处理-OpenCVSharp教程(八) OpenCVSharp基本绘图函数:绘制线、圆、矩形、椭圆、文字等

    本文作者Color Space,文章未经作者允许禁止转载! 本文将介绍OpenCVSharp基本绘图函数:绘制线.圆.矩形.椭圆.文字等! 实现效果: 代码演示: using System; usin ...

最新文章

  1. Linux运维:查看磁盘空间的大小
  2. 同方自主可控系统服务器,自主可控 同方超强TR1210服务器!
  3. centos 7 局域网丢包排查_宽带发生故障怎么办?教你几招轻松排查
  4. mysql安全权限的讲解
  5. java笔试题递归,递归(网上搜的一些笔试题)
  6. Ubuntu16.04+Cuda8.0+cuDNN6配置py-faster rcnn(转)
  7. Linux 中的旧扣簧键盘(Bucklespring 键盘)声音
  8. mysql与nagios的结合使用
  9. keras训练cifar10数据集源代码
  10. 虚拟机Ubuntu复制粘贴到主机(不安装vmware-tools实现两者之间文件共享)
  11. 水星怎么设置网速最快_水星怎么设置路由器的网速
  12. Django中关于URL配置文件urls.py的理解
  13. 四字母net域名值钱吗?四字母域名取名有什么技巧?
  14. qt中将按钮指向的鼠标变成手型
  15. Intel Compute Sticks 电脑棒安装Ubuntu
  16. 爱心之火成燎原之势——浩荣 成都传智播客支持你
  17. 研发过程管理与质量控制
  18. python登录教务系统_python+mysql实现教务管理系统
  19. php新闻网站毕设论文,校园新闻发布毕业设计网站php实现
  20. python用禁忌搜索算法实现TSP旅行商问题

热门文章

  1. 对数幅度谱图像matlab,fft2绘制图像的对数幅度谱,比较图像旋转平移和缩放后的频谱.doc...
  2. 2019中兴捧月·初赛心得
  3. LeapFTP 显示服务器端文件名乱码问题处理
  4. c语言邮递员问题算法,中国邮递员问题的求解实例
  5. 转:读“DataBase Sharding at Netlog”,看DataBase Scale Out
  6. 趣味python编程之经典俄罗斯方块
  7. 16Aspx.com源码2013年10月到2013年12月详细
  8. 记录一下,不是所有的韩语输入法apk,装到同一个系统,都能弹出软键盘
  9. 巴比特 | 元宇宙每日必读:红杉中国“雇”了一位虚拟员工,自称每秒可看百份商业计划书,期待时薪为0.68元...
  10. 开源房产中介管理系统