1

I have a set of rectangles and i need to calculate the manhatten distance between them. I already tried to implement it, but the code blow up and did not work well.

Maybe someone could help me with some smart (and efficient) formulas which could be used to calculate the distance between two rectangles?

Examples:

enter image description here

The distance between A and B is the length of the line 1. The distance between A and C is the length of the line 2. etc.

I use python to implement everything. If already a function exists (e.g. in scipy) and someone knows it, this would also be great.

Thank you

Kevin Meier
  • 2,339
  • 3
  • 25
  • 52
  • There is already a question with an answer: http://stackoverflow.com/questions/8224470/calculating-manhattan-distance#8224516 It may be java but it is easily changed into python – Headhunter Xamd Nov 10 '16 at 08:37
  • i find the python one http://stackoverflow.com/questions/35363811/manhattan-distance-python – Ari Gold Nov 10 '16 at 08:41
  • this won't directly help since you only get the distance between nodes. Unfortunately, the minimal distance between two rectangles will only be the distance between the closest corner points, if one rectange is not in the "shadow" of another. However if this is not the case, you need to project on the only possible edge and get potentially two intermediate points to determine the distance then. Btw, in that case the distance will be the euclidean distance – Quickbeam2k1 Nov 10 '16 at 08:45
  • Unfortunately both questions ask for the manhatten distance between two points which is quite easy. With rectangles it is much more complicated as can be seen on the example image. – Kevin Meier Nov 10 '16 at 08:45

2 Answers2

3

I suggest, you work with the central points of rectangles and rectangle widths to compute the distances. You mainly have to figure out which corners (edges) of rectangles to use for computation. everything else is simple. A quick example:

class Rect:
    def __init__(self,cpt,w,h):
        self.x = cpt[0]
        self.y = cpt[1]
        self.w = w
        self.h = h

    def dist(self,other):
        #overlaps in x or y:
        if abs(self.x - other.x) <= (self.w + other.w):
            dx = 0;
        else:
            dx = abs(self.x - other.x) - (self.w + other.w)
        #
        if abs(self.y - other.y) <= (self.h + other.h):
            dy = 0;
        else:
            dy = abs(self.y - other.y) - (self.h + other.h)
        return dx + dy

#example:
A = Rect((0,0),2,1)
B = Rect((4,5),1,2)
C = Rect((-1,-5),1,1)

print(A.dist(C))
print(A.dist(B))
print(B.dist(C))
LynxLike
  • 391
  • 5
  • 10
  • I played around with you code and it seems to work:) Thanks! – Kevin Meier Nov 10 '16 at 09:11
  • 3
    you can also fairly easy get the "real/diagonal" distance between rectangles, just change the return statement to: `return sqrt(dx**2 + dy**2)` and use math or numpy module for the root – LynxLike Nov 10 '16 at 09:17
  • Thanks for this additional hint:) Btw: i really like your code. It is quite simple and also easy to understand. – Kevin Meier Nov 10 '16 at 09:30
  • 1
    Thanks for +1! I know it is easy to get bogged down and try to determine the exact edge on both rectangles to calculate the distance, since this is how one thinks about it. I generally dislike such complications and try to find a cleaner way and today I got lucky ;) – LynxLike Nov 10 '16 at 11:15
  • 1
    Great algorithm! I assume by "central points of rectangles and rectangle widths" you mean their center and the width/height from the center to the edge or basically just the normal width divided by two, don't you? Also, for an example it's of course more than good enough but it could be a bit more efficient if you'd just set `dx = abs(self.x - other.x) - (self.w + other.w)` and then check if that's less than 0 and if it is set it to 0. Otherwise, the calculation has to be done twice if the condition is false. – Stefan Fabian Nov 17 '17 at 23:48
1

A solution by @LynxLike does not seem to work so I am posting another solution. The test cases at the end of this post are simplified segments distances, where the answer of each case is obvious and the previous solution fails some.

In general, while the problem looks complicated, you can simplify it by dividing the problem into x-segments and y-segments and computing separately, by using the same algorithm for both axis. A solution should work both for rectangles and line segments.

For dx, you compute if the x coordinate of the right rect (or line seg) is larger than the sum of the left rect's x coord and width. If it's positive, that means there is a distance; otherwise, it's either touching, overlapping (in x-axis), or rects' relative position is flipped. Basically, unless rects have some distance you will get 0 or negative results which you can easily clip to 0 using a max function. You do not need to check which rect is which side; you can again use max() to check both situations at the same time.

# assuming other is on the right
d_case1 = max(other.x - (self.x+self.w), 0)
# assuming self is on the right
d_case2 = max(self.x - (other.x+other.w), 0)
# checking if any distance has positive
dx = max(d_case1, d_case2)

Here is a complete solution, which is based on @LynxLike 's Rect class and fixes where the original conditional statements failed! Thanks!

class Rect:
    def __init__(self, cpt, w, h):
        self.x = cpt[0]
        self.y = cpt[1]
        self.w = w
        self.h = h

    def dist(self, other):
        dx = max(max(other.x - (self.x+self.w), 0),
                 max(self.x - (other.x+other.w), 0))
        dy = max(max(other.y - (self.y+self.h), 0),
                 max(self.y - (other.y+other.h), 0))
        return dx + dy

# Case1: 1 distance apart
A = Rect((1,0),3,0)  #| ---
B = Rect((5,0),3,0)  #|     ---
assert A.dist(B) == 1
assert B.dist(A) == 1

# Case2: touching
A = Rect((1,0),4,0)  #| ----
B = Rect((5,0),3,0)  #|     ---
assert A.dist(B) == 0
assert B.dist(A) == 0

# Case3: intersects
A = Rect((1,0),5,0)  #| -----
B = Rect((5,0),3,0)  #|     ---
assert A.dist(B) == 0
assert B.dist(A) == 0

# Case4: 1 distance apart in negative domain
A = Rect((-1,0),3,0)  #      -|-- 
B = Rect((-5,0),3,0)  #  ---
assert A.dist(B) == 1
assert B.dist(A) == 1
Naofumi
  • 331
  • 4
  • 9