2013-12-08 165 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