如何计算一个点到矩形的最近点的距离?

3
我在二维坐标系中有一个轴对齐的矩形,其由左下角点和右上角点表示,以及一个可能在矩形内外的点。我想找到该点到矩形最近点的距离,无论它是否在矩形内部。当然,我可以使用9种不同结果的开关语句来解决问题,但我希望有一种更简洁的解决方案。
此外,我已经找到了多个解决这个问题的方案(比如这一个),但所有这些方法都会在点在包围盒内时将距离计算为0,而我不想要这样的结果。

“点到矩形最近边的距离”与“点到矩形最近的距离”完全相同吗?如果不同,有什么区别? - n. m. could be an AI
@n.m. 这正是我想表达的。我修改了标题。 - ddominnik
一个矩形由4条边组成,因此你可以找到点到每条边的绝对距离,并取这四个距离中的最小值。无论点位于矩形内部还是外部,这种方法都能有效运作。 - Matthew Pope
@MatthewPope 一个矩形由四个“线段”组成,而不是四条直线。你提供的链接根本没有讨论线段。 - n. m. could be an AI
对不起,这是我完全失误了。这就是我晚上回答问题的下场。你可以通过找到点在延长线上的投影来计算点到线段的距离。从两个端点和刚刚找到的投影点中选择落在其他两个点之间的点。这就是离原始点最近的线段上的点。找到线段上最近点与原始点之间的距离。对所有线段重复此过程并取最小距离。 - Matthew Pope
3个回答

5
我的回答比其他人稍微长一些,但它来自不同的角度。
关键不在于你是否在“矩形”内部,而是你是否在由矩形边界无限延伸形成的走廊内(想象一个无限加号,以矩形为中心)。
如果在这些走廊内,那么最近的距离是垂直于其中一条边的距离。
如果在外面,那么最近的距离就是到最近角落的距离。
你的代码可能看起来像这样:
nearest_distance(rectangle, point):
    d_top = abs(rectangle.top - point.y)
    d_bottom = abs(rectangle.bottom - point.y)
    corner_y = d_top < d_bottom ? rectangle.top : rectangle.bottom

    d_left = abs(rectangle.left - point.x)
    d_right = abs(rectangle.right - point.x)
    corner_x = d_left < d_right ? rectangle.left : rectangle.right

    d_cx = corner_x - point.x
    d_cy = corner_y - point.y
    d_corner = sqrt(d_cx*d_cx + d_cy*d_cy)

    return min(d_top, d_bottom, d_left, d_right, d_corner)

如果你想尝试保存一个sqrt,你可以检查你是否在走廊内或外。在这种情况下,你将重新排列它如下:

nearest_distance(rectangle, point):
    d_top = abs(rectangle.top - point.y)
    d_bottom = abs(rectangle.bottom - point.y)
    d_left = abs(rectangle.left - point.x)
    d_right = abs(rectangle.right - point.x)

    r = rectangle # just to make the next line neater
    if r.left <= point.x <= r.right or r.bottom <= point.y <= r.top:
        return min(d_top, d_bottom, d_left, d_right)
    else:
        corner_y = d_top < d_bottom ? rectangle.top : rectangle.bottom
        corner_x = d_left < d_right ? rectangle.left : rectangle.right

        d_cx = corner_x - point.x
        d_cy = corner_y - point.y
        d_corner = sqrt(d_cx*d_cx + d_cy*d_cy)
        return d_corner

1
这是我最初想解决这个问题的方式,但当我看了OP链接的答案后,我选择了那个。不错! - Sani Huttunen
非常好的答案,非常感谢!我真的没有考虑过如何以不同于链接中的方式分解问题。 - ddominnik

1
你可以扩展MultiRRomero的链接解决方案,并对矩形内的点进行一些额外的计算。
对于这些点,矩形边界上最近的点具有与该点相同的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);
  var distance = Math.sqrt(dx*dx + dy*dy)
  if (distance == 0) {
    distance = Math.min(p.x - rect.min.x, rect.max.x - p.x, p.y - rect.min.y, rect.max.y - p.y)
  }
  return distance
}

编辑:错别字已更正

0

这样怎么样?(第一部分“借鉴”了MultiRRomeros的answer

function distance(rect, p) {
  // outside
  var dxo = Math.max(rect.min.x - p.x, 0, p.x - rect.max.x);
  var dyo = Math.max(rect.min.y - p.y, 0, p.y - rect.max.y);
  var hypothenuse = Math.sqrt(dxo*dxo + dyo*dyo);

  // inside
  var dxi = Math.min(rect.max.x - p.x, p.x - rect.min.x);
  var dyi = Math.min(rect.max.y - p.y, p.y - rect.min.y);

  return hypothenuse > 0 ? hypothenuse : Math.min(dxi, dyi);
}

1
@ScottMermelstein:当point在矩形内部时,hypothenuse将为0,因为dxodxy也与0进行比较。所以当点在矩形内部时,0将是最大的数。即rect.min.x - p.x等将为0或更小。 - Sani Huttunen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接