2013-12-08 125 views
4

这是一个语言不可知的问题。给定与l,t,w,h(左,顶部,宽度,高度)和点x,y矩形的尺寸,我如何找到该点矩形周长的最近点?如何查找矩形周长到给定点的最近点?

我试图解决它在Lua,但任何其他语言会做。到目前为止,这是我最大的努力:

local function nearest(x, a, b) 
    if a <= x and x <= b then 
    return x 
    elseif math.abs(a - x) < math.abs(b - x) then 
    return a 
    else 
    return b 
    end 
end 

local function getNearestPointInPerimeter(l,t,w,h, x,y) 
    return nearest(x, l, l+w), nearest(y, t, t+h) 
end 

这适用于一个点外周边的或在外围本身。但对于内周边的点它失败(它只是返回x,y

我的直觉告诉我,该解决方案应该是简单的,但我似乎还没有找到它。

回答

11

这次我试图抓住矩形任何一侧的点的最小距离。

local abs, min, max = math.abs, math.min, math.max 

local function clamp(x, lower, upper) 
    return max(lower, min(upper, x)) 
end 

local function getNearestPointInPerimeter(l,t,w,h, x,y) 
    local r, b = l+w, t+h 

    x, y = clamp(x, l, r), clamp(y, t, b) 

    local dl, dr, dt, db = abs(x-l), abs(x-r), abs(y-t), abs(y-b) 
    local m = min(dl, dr, dt, db) 

    if m == dt then return x, t end 
    if m == db then return x, b end 
    if m == dl then return l, y end 
    return r, y 
end 
+0

不幸的是,它没有。你的算法返回最近的* corner *的坐标。周边的最近点并不总是其中一个角落。 – kikito

+0

我用另一种算法编辑了我的答案。我在http://www.lua.org/cgi-bin/demo上用一些值对它进行了测试,它似乎运行良好。 – Keeper

+0

对不起,我不认为它的作品。我得到非常随机的值 - 有时甚至在矩形本身之外。 – kikito

1

设C1,C2,C3,C4为矩形的顶点。
从给定的A点开始,绘制2条线,这两条线垂直于矩形的两边,即
。设B1,B2,B3,B4,
, 是它们与由矩形的两边确定的线的交点(这些Bk中的一些可能也与
一致,例如,如果对于某个k,A = Ck)。您的解决方案是Bk
或其中一个点Ck,只是强力检查8个点
(同样,这8个点中的一些可能重合,但没关系)中的一个。

+0

部分或全部也可能无法存在,考虑矩形0,0,1,1和2,2点OK – mornfall

+0

,我编辑我的回答,我相信这个工程。我只需要证明它:)除非我的直觉对我有所诡计,否则这不应该太难。 –

+0

是的,好吧,如果将飞机划分为9个扇区(如果一个人延长矩形的边长,会发生这种情况)并认为A点可能在哪里(在哪个扇区中),证明是微不足道的。 –

1

另一种可能的算法(类似我的第一答案)可以在这里找到 - 从Dinre之一。

Calculating the distance between polygon and point in R

看起来很简单,实际上它是我的第一个回答的简化(也许更好)的版本在这里。

找到两个最近的矩形的顶点Ci和Cj为给定的点A.

查找点M,其中从A垂直线到线(CI,CJ)穿过线(CI,CJ)。

你的解决方案是将C1或C或M.

在我看来,这样适用于所有情况下(不管A点位于平面)。

+0

这是一个很好的选择。我相信这就是@Keeper对他的回答所做的(含蓄)。 – kikito

+0

我没有看这段代码。很可能它是如此。如果你使用这个算法,实现应该是微不足道的。我可以用Java或C#编写它,或者在一段时间后我会知道它);我真的不知道你的语言。无论如何,祝你好运。 –

0

你在找这样的吗? 灵感来自守护者代码:

local function getNearestPointInPerimeter(l,t,w,h, x,y) 
    -- x axis increases to the right 
    -- y axis increases down 
    local r = l + w 
    local b = t + h 
    local inside = true -- unless later proven otherwise 
    -- if the point (x,y) is outside the rectangle, 
    -- push it once to the nearest point on the perimeter, or 
    -- push it twice to the nearest corner. 
    if x < l then x = l; inside = false; end 
    if x > r then x = r; inside = false; end 
    if y < t then y = t; inside = false; end 
    if y > b then y = b; inside = false; end 
    -- if the point (x,y) is inside the rectangle, 
    -- push it once to the closest side. 
    if inside then 
     local dt = math.abs (y - t) 
     local db = math.abs (y - b) 
     local dl = math.abs (x - l) 
     local dr = math.abs (x - r) 
     if dt <= db and dt <= dl and dt <= dr then 
     y = t 
     elseif db <= dl and db <= dr then 
     y = b 
     elseif dl <= dr then 
     x = l 
     else 
     x = r 
     end 
    end 
    return x,y 
end 
+0

谢谢你的回答,我会带着老板的回答。 – kikito

0

谢谢你的问题和答案!这里是我选择的答案的Python翻译版本,以防万一需要。唯一的定制部分是使用lambda的内嵌函数定义。

我在Qt的QRect和QPoint的GUI中成功地使用了它,以确保在QGraphcsView中显示了某些内容。

def getNearestPointInPerimeter(self, left, top, width, height, x, y): 
    right = left + width 
    bottom = top + height 

    clamp = lambda value, minv, maxv: max(min(value, maxv), minv) 
    x = clamp(x, left, right) 
    y = clamp(y, top, bottom) 

    dl = abs(x - left) 
    dr = abs(x - right) 
    dt = abs(y - top) 
    db = abs(y - bottom) 
    m = min(dl, dr, dt, db) 

    if m == dt: 
     result = (x, top) 
    elif m == db: 
     result = (x, bottom) 
    elif m == dl: 
     result = (left, y) 
    else: 
     result = (right, y) 

    return result