计算一个点和一个矩形框之间的距离(最近点)

时间:2011-03-10 02:37:54

标签: 3d geometry distance

有一个简单的公式可以计算出来吗?我一直在研究一些数学但是我只能找到一种方法来计算指向盒子中心的距离,而不是指向最近的点。这个问题是否有一些资源?

9 个答案:

答案 0 :(得分:46)

这是一个避免所有案例逻辑的公式。 (我现在正好在JS工作,所以这是一个JS实现)。设rect = {max:{x:_, y:_}, min:{x:_, y:_}}p={x:_, y:_}

function distance(rect, p) {
  var dx = Math.max(rect.min.x - p.x, 0, p.x - rect.max.x);
  var dy = Math.max(rect.min.y - p.y, 0, p.y - rect.max.y);
  return Math.sqrt(dx*dx + dy*dy);
}

说明: 这会将问题分解为计算x距离dx和y距离dy。然后使用距离公式。

为了计算dx,这是如何工作的。 (dy类似)

查看提供给max函数的元组:(min-p, 0, p-max)。让我们指定这个元组(a,b,c)

如果p为min,则我们得到p < min&lt; max,表示元组将评估为(+,0,-),因此max函数将正确返回a = min - p

如果p在min和max之间,那么我们有min&lt; p&lt; max,表示元组将评估为(-,0,-)。同样,max函数将正确返回b = 0

最后,如果p在max的右边,那么我们有,min&lt;最大&lt; p,元组的计算结果为(-,0,+)。再次,Math.max正确返回c = p - max

所以事实证明,所有案例逻辑都由Math.max处理,这导致了一个不错的3行,无控制流功能。

答案 1 :(得分:7)

我认为你需要分析案例;没有单一的公式。从两个方面说明更容易:

1          2          3
    +-------------+
    |             |
4   |      0      |   5
    |             |
    +-------------+
6          7          8

盒子的边缘(延伸)将外部分成9个区域。区域0(框内)通过计算到每条边的距离并取最小值来求解。区域1中的每个点最接近左上顶点,对于区域3,6和8也类似。对于区域2,4,5和7,您需要找到从该点到最近边缘的距离,是一个相当简单的问题。您可以通过对每个边缘进行分类来确定点所在的区域。 (通过指示边缘,比如逆时针方向,更容易看到如何做到这一点。)这也会告诉你这个点是否在框内。

在3D中,逻辑完全相同,只是你根据六个面进行分类并且你有更多的情况。

如果框的边缘与坐标轴平行,问题就更简单了。

答案 2 :(得分:5)

假设该点名为PABCD是我们的矩形。然后问题可以分解为以下子问题:

(1)开发一个函数dist(P, AB),用于计算点P与任意 AB之间的距离。

(2)计算点P与矩形每一边(每边是一个线段)之间的四个距离,并取四个中最短的

  distance = min(dist(P, AB), dist(P,BC), dist(P, CD), dist(P, DA))

这是你的答案。

现在,我们需要知道如何计算点P和任意线段AB之间的距离,即如何计算dist(P, AB)。这完成如下

(1)执行点P与线AB的垂直投影。您在P'上获得了新点AB

(2)如果P'位于AB之间,那么dist(P, AB)就是PP'之间的距离{1}}。

(3)否则,dist(P, AB)PAB之间的距离,以较短者为准。

就是这样。有一些明显的方法来优化程序,但即使按字面意思实现,它也会很好地工作。

P.S。当然,人们可以询问如何执行点到线的投影。我会把它作为练习留给读者:)

答案 3 :(得分:1)

Kikito的答案是不正确的,事实上如果P在Ted Hopp方案的区域2,4,5或7中,它返回距顶点的最小距离,这距离顶点的最小距离不同(更大)边缘。

我会通过返回0而不是min(p - lower,upper - p)来修复kikito的函数distance_aux,并且一切都与P在框内的0区域分开。 在我看来,区域应该单独管理,取决于你想要达到的目的,是距离区域的距离还是距离盒子周边的距离。 如果你想获得距离盒子区域的距离,我会说当盒子里面的点是零。

function inside(point, box)
    return (point.x > box.left AND point.x < box.right AND point.y > box.top AND point.y < box.bottom)
end

function distance_aux(p, lower, upper)
    if p < lower then return lower - p end
    if p > upper then return p - upper end
    return 0
end

function distance(point, box)
    local dx = distance_aux(point.x, box.left, box.right)
    local dy = distance_aux(point.y, box.top, box.bottom)
    if (inside(point, box))
        return min(dx, dy)    // or 0 in case of distance from the area
    else
        return sqrt(dx * dx + dy * dy)
    endif
end

答案 4 :(得分:1)

轻微优化的C#替代方案(尽管在将双精度数与0进行比较时可能存在一些容差)。我还建议为这些创建一些Rect或Point扩展方法。

public static class GeometryUtils
{
    public static double Distance(Point point, Rect rect)
    {
        var xDist = MinXDistance(point, rect);
        var yDist = MinYDistance(point, rect);
        if (xDist == 0)
        {
            return yDist;
        }
        else if (yDist == 0)
        {
            return xDist;
        }

        return Math.Sqrt(Math.Pow(xDist, 2) + Math.Pow(yDist, 2));
    }

    private static double MinXDistance(Point point, Rect rect)
    {
        if (rect.Left > point.X)
        {
            return rect.Left - point.X;
        }
        else if (rect.Right < point.X)
        {
            return point.X - rect.Right;
        }
        else
        {
            return 0;
        }
    }

    private static double MinYDistance(Point point, Rect rect)
    {
        if (rect.Bottom < point.Y)
        {
            return point.Y - rect.Bottom;
        }
        else if (rect.Top > point.Y)
        {
            return rect.Top - point.Y;
        }
        else
        {
            return 0;
        }
    }
}

答案 5 :(得分:0)

这是3D盒子还是2D矩形? 无论哪种方式,你最好得到每一方的point-line(2D)或point-plane(3D)距离,然后选择最小值。

修改:有一种更好的方式描述here(上一篇文章)。它涉及将您的点坐标转换为框空间,然后使用框大小“饱和”坐标以找到最靠近该点的框上的点。我没试过,但看起来对我来说。

答案 6 :(得分:0)

我一直在寻找这个,我想我有一个解决方案,对于盒子是轴对齐的情况(一个相当常见的情况)

我相信在这种情况下你可以像这样计算距离:

function distance_aux(p, lower, upper)
  if p < lower then return lower - p end
  if p > upper then return p - upper end
  return min(p - lower, upper - p)
end

function distance(point, box)
  local dx = distance_aux(point.x, box.left, box.right)
  local dy = distance_aux(point.y, box.top, box.bottom)
  return sqrt(dx * dx + dy * dy)
end

当然,这可以扩展到z。

答案 7 :(得分:0)

对于AABB:

也许不是表现最好的,但肯定是最简单的方法:

p =你的观点

c =立方体的中心

s =立方体的一半大小

r =我们正在寻找的点

v = p - c;
m = max_abs(v);
r = c + ( v / m * s );

答案 8 :(得分:0)

使用点积很容易实现。实际上,已经在3d中回答了非轴对齐情况。

https://stackoverflow.com/a/44824522/158285

但在2D中你可以实现相同的

public struct Vector2 {
   double X; double Y

   // Vector dot product
   double Dot(Vector2 other)=>X*other.X+Y*other.Y;


   // Length squared of the vector
   double LengthSquared()=>Dot(this,this);

   // Plus other methods for multiplying by a scalar
   // adding and subtracting vectors etc
}

返回最近点的函数

public Vector2 ClosestPointTo
    (Vector2 q, Vector2 origin, Vector3 v10, Vector3 v01)
{
    var px = v10;
    var py = v01;


    var vx = (px - origin);
    var vy = (py - origin);


    var tx = Vector2.Dot( q - origin, vx ) / vx.LengthSquared();
    var ty = Vector3.Dot( q - origin, vy ) / vy.LengthSquared();


    tx = tx < 0 ? 0 : tx > 1 ? 1 : tx;
    ty = ty < 0 ? 0 : ty > 1 ? 1 : ty;

    var p = tx * vx + ty * vy + origin;

    return p;
}