点到圆心的距离公式,快速计算点到圆心的距离方法

各位朋友,我是专注于前端技术的西瓜哥。

今天我们要深入探讨平面几何算法中的一个重要内容:如何计算点到直线以及圆的最近距离点。

这种算法在实际应用中非常实用。

以图形拾取为例,特别是在处理只有轮廓线没有填充色的图形时,如果鼠标点击的位置与最近点的距离小于预设的容差值,就可以认为该图形已被选中。

此外,在图形编辑软件中,实体吸附、极轴辅助和正交约束等功能也依赖于此类算法。当鼠标指针接近某条直线时,绘图操作会自动将点吸附到该直线的最近位置上。

求解最近点的函数通常命名为 getClosestPoint(获取最近点)或 project(投影)。

在详细讲解投影算法之前,我们需要先掌握一个基础概念:线性插值。

在计算机图形学中,我们常常用两个点来定义一条线段,因为通过这两个点以及线性插值的方法,可以生成线段上所有的中间点,从而完成线段的绘制。

这可以类比于 Flash 动画中的补间动画效果。

假设我们有两个点 p0 和 p1,现在需要找到线段上比例参数为 t 的点 p。

这个点 p 位于 p0 到 p1 的方向上,且与 p0 的距离占 p0 到 p1 距离的比例为 t(即 t = 距离(p0, p) / 距离(p0, p1)),其中 t 的取值范围是 0 到 1。

因此,我们可以得到如下的计算公式:

这个公式可以从向量的角度进行理解。

向量可以表示为其单位方向向量乘以向量的模(即向量的长度)。

当我们乘以 t 时,相当于先将 p0 到 p1 的向量除以 距离(p0, p1) 得到一个单位方向向量,然后再乘以 距离(p0, p),这样就得到了从 p0 到 p 的向量,这个向量实际上就是偏移量,将这个偏移量加到 p0 上,就可以得到插值点 p。

线性插值在数学和计算机图形学领域有着广泛的应用,例如贝塞尔曲线,线性贝塞尔曲线就是基于线性插值构建的,此外,我们后面要讲的最近点算法也大量使用了线性插值的思想。

如果我们将多个线段依次连接,并在连接过程中不断进行线性插值,然后将生成的点继续连接,并再次进行插值,如此循环,直到点的数量减少到一个,这样就可以构建出 N 阶贝塞尔曲线,如下图所示:

在上面的讨论中,我们限制了 t 的取值范围在 0 到 1 之间,这是因为我们只考虑了在两点之间的插值情况。实际上,t 可以取任意值(包括负值)。

当然,在平面几何中,t 取任意值会导致点位于线段之外,但它仍然满足点位于直线上的特性,如下图所示:

现在,我们来解决如何找到直线上距离点 p 最近的点的问题。

这个问题可以通过线性插值来解决,关键在于计算 t 的值。

t 代表的是 p0 到最近点的距离占 p0 到 p1 距离的比例。

由于 p0 到最近点的距离是未知的,我们可以利用点积公式来计算 p0 到 p 向量在 p0 到 p1 向量上的投影长度。

点积公式如下:

|A| 表示向量 A 的长度,可以通过勾股定理来计算:

A·B 表示两个向量的 x 和 y 坐标分别相乘后求和。

|A| cos(θ) 表示向量 A 在向量 B 上的投影长度,即:

根据前面的讨论,我们知道 t 等于 p0 到最近点的距离除以 p0 到 p1 的距离。因此,t 的计算公式为:

需要注意的是,投影是有方向的,因此 t 可能是负值。

此外,当直线两端的点重合时,问题会退化为一个点。只有两个不同的点才能唯一确定一条直线。

函数的返回值除了最近点的坐标外,还会额外返回 t 值和最短距离 d。

返回 t 值是因为有时我们需要保存这个比例值,或者将其用于更复杂的算法计算。

最短距离 d 可以不返回,如果需要的话,可以在外部重新计算。d 值可以用于实现高精度的拾取算法,当 d 小于某个阈值时,可以认为线段被选中。

我制作了一个可视化交互演示。

演示地址为:

https://codepen.io/F-star/pen/RwdzMwz

计算圆上最近点的原理与直线类似,同样需要计算 t。

对于圆,t 的计算公式为 radius / distance(center, p)

这里需要注意,除数不能为 0,如果 distance(center, p) 为 0,则 t 应直接设置为 0。

在圆的情况下,t 不会是负数,因为是从圆心向外辐射,无法取负值。

演示地址为:

https://codepen.io/F-star/pen/PoLreNJ

今天我们学习了如何计算点到直线和圆的最近点,不知道大家是否已经掌握。

此外,还有其他图形的最近点计算问题,例如圆弧(有两种表示方式),只需要再增加一个判断点是否在圆弧上的逻辑即可。此外还有贝塞尔曲线等,我们将在后续文章中继续探讨。

这里推荐两个计算复杂曲线最近点的库。

Bezier.js 用于计算贝塞尔曲线的最近点:https://pomax.github.io/bezierjs/#project

verb-nurbs-web 库中的 NurbsCurve 功能可以计算样条曲线的最近点:http://verbnurbs.com/docs/geom/NurbsCurve/#closestpoint

我是前端西瓜哥,关注我,一起探索更多平面几何的奥秘。