247 lines
5.8 KiB
Python
247 lines
5.8 KiB
Python
|
import unittest
|
||
|
|
||
|
|
||
|
################################################################################
|
||
|
# Solution
|
||
|
################################################################################
|
||
|
# bottom :: Rectangle -> Int
|
||
|
def bottom(x):
|
||
|
return x.get('bottom_y')
|
||
|
|
||
|
|
||
|
# top :: Rectangle -> Int
|
||
|
def top(x):
|
||
|
return bottom(x) + x.get('height')
|
||
|
|
||
|
|
||
|
# left :: Rectangle -> Int
|
||
|
def left(x):
|
||
|
return x.get('left_x')
|
||
|
|
||
|
|
||
|
# right :: Rectangle -> Int
|
||
|
def right(x):
|
||
|
return left(x) + x.get('width')
|
||
|
|
||
|
|
||
|
# sort_highest :: Rectangle -> Rectangle -> (Rectangle, Rectangle)
|
||
|
def sort_highest(x, y):
|
||
|
if top(x) >= top(y):
|
||
|
return x, y
|
||
|
else:
|
||
|
return y, x
|
||
|
|
||
|
|
||
|
# sort_leftmost :: Rectangle -> Rectangle -> (Rectangle, Rectangle)
|
||
|
def sort_leftmost(x, y):
|
||
|
if left(x) <= left(y):
|
||
|
return x, y
|
||
|
else:
|
||
|
return y, x
|
||
|
|
||
|
|
||
|
# rectify :: Int -> Int -> Int -> Int -> Rectify
|
||
|
def rectify(top=None, bottom=None, left=None, right=None):
|
||
|
assert top >= bottom
|
||
|
assert left <= right
|
||
|
return {
|
||
|
'left_x': left,
|
||
|
'bottom_y': bottom,
|
||
|
'width': right - left,
|
||
|
'height': top - bottom,
|
||
|
}
|
||
|
|
||
|
|
||
|
# empty_rect :: Rectangle
|
||
|
def empty_rect():
|
||
|
return {
|
||
|
'left_x': None,
|
||
|
'bottom_y': None,
|
||
|
'width': None,
|
||
|
'height': None,
|
||
|
}
|
||
|
|
||
|
|
||
|
# find_rectangular_overlap :: Rectangle -> Rectangle -> Maybe(Rectangle)
|
||
|
def find_rectangular_overlap(x, y):
|
||
|
ha, hb = sort_highest(x, y)
|
||
|
la, lb = sort_leftmost(x, y)
|
||
|
|
||
|
if bottom(hb) <= top(hb) <= bottom(ha) <= top(ha):
|
||
|
return empty_rect()
|
||
|
|
||
|
if left(la) <= right(la) <= left(lb) <= right(lb):
|
||
|
return empty_rect()
|
||
|
|
||
|
# We should have an intersection here.
|
||
|
verts = [bottom(ha), top(ha), bottom(hb), top(hb)]
|
||
|
verts.sort()
|
||
|
horzs = [left(la), right(la), left(lb), right(lb)]
|
||
|
horzs.sort()
|
||
|
return rectify(top=verts[2],
|
||
|
bottom=verts[1],
|
||
|
left=horzs[1],
|
||
|
right=horzs[2])
|
||
|
|
||
|
|
||
|
################################################################################
|
||
|
# Tests
|
||
|
################################################################################
|
||
|
class Test(unittest.TestCase):
|
||
|
def test_overlap_along_both_axes(self):
|
||
|
rect1 = {
|
||
|
'left_x': 1,
|
||
|
'bottom_y': 1,
|
||
|
'width': 6,
|
||
|
'height': 3,
|
||
|
}
|
||
|
rect2 = {
|
||
|
'left_x': 5,
|
||
|
'bottom_y': 2,
|
||
|
'width': 3,
|
||
|
'height': 6,
|
||
|
}
|
||
|
expected = {
|
||
|
'left_x': 5,
|
||
|
'bottom_y': 2,
|
||
|
'width': 2,
|
||
|
'height': 2,
|
||
|
}
|
||
|
actual = find_rectangular_overlap(rect1, rect2)
|
||
|
self.assertEqual(actual, expected)
|
||
|
|
||
|
def test_one_rectangle_inside_another(self):
|
||
|
rect1 = {
|
||
|
'left_x': 1,
|
||
|
'bottom_y': 1,
|
||
|
'width': 6,
|
||
|
'height': 6,
|
||
|
}
|
||
|
rect2 = {
|
||
|
'left_x': 3,
|
||
|
'bottom_y': 3,
|
||
|
'width': 2,
|
||
|
'height': 2,
|
||
|
}
|
||
|
expected = {
|
||
|
'left_x': 3,
|
||
|
'bottom_y': 3,
|
||
|
'width': 2,
|
||
|
'height': 2,
|
||
|
}
|
||
|
actual = find_rectangular_overlap(rect1, rect2)
|
||
|
self.assertEqual(actual, expected)
|
||
|
|
||
|
def test_both_rectangles_the_same(self):
|
||
|
rect1 = {
|
||
|
'left_x': 2,
|
||
|
'bottom_y': 2,
|
||
|
'width': 4,
|
||
|
'height': 4,
|
||
|
}
|
||
|
rect2 = {
|
||
|
'left_x': 2,
|
||
|
'bottom_y': 2,
|
||
|
'width': 4,
|
||
|
'height': 4,
|
||
|
}
|
||
|
expected = {
|
||
|
'left_x': 2,
|
||
|
'bottom_y': 2,
|
||
|
'width': 4,
|
||
|
'height': 4,
|
||
|
}
|
||
|
actual = find_rectangular_overlap(rect1, rect2)
|
||
|
self.assertEqual(actual, expected)
|
||
|
|
||
|
def test_touch_on_horizontal_edge(self):
|
||
|
rect1 = {
|
||
|
'left_x': 1,
|
||
|
'bottom_y': 2,
|
||
|
'width': 3,
|
||
|
'height': 4,
|
||
|
}
|
||
|
rect2 = {
|
||
|
'left_x': 2,
|
||
|
'bottom_y': 6,
|
||
|
'width': 2,
|
||
|
'height': 2,
|
||
|
}
|
||
|
expected = {
|
||
|
'left_x': None,
|
||
|
'bottom_y': None,
|
||
|
'width': None,
|
||
|
'height': None,
|
||
|
}
|
||
|
actual = find_rectangular_overlap(rect1, rect2)
|
||
|
self.assertEqual(actual, expected)
|
||
|
|
||
|
def test_touch_on_vertical_edge(self):
|
||
|
rect1 = {
|
||
|
'left_x': 1,
|
||
|
'bottom_y': 2,
|
||
|
'width': 3,
|
||
|
'height': 4,
|
||
|
}
|
||
|
rect2 = {
|
||
|
'left_x': 4,
|
||
|
'bottom_y': 3,
|
||
|
'width': 2,
|
||
|
'height': 2,
|
||
|
}
|
||
|
expected = {
|
||
|
'left_x': None,
|
||
|
'bottom_y': None,
|
||
|
'width': None,
|
||
|
'height': None,
|
||
|
}
|
||
|
actual = find_rectangular_overlap(rect1, rect2)
|
||
|
self.assertEqual(actual, expected)
|
||
|
|
||
|
def test_touch_at_a_corner(self):
|
||
|
rect1 = {
|
||
|
'left_x': 1,
|
||
|
'bottom_y': 1,
|
||
|
'width': 2,
|
||
|
'height': 2,
|
||
|
}
|
||
|
rect2 = {
|
||
|
'left_x': 3,
|
||
|
'bottom_y': 3,
|
||
|
'width': 2,
|
||
|
'height': 2,
|
||
|
}
|
||
|
expected = {
|
||
|
'left_x': None,
|
||
|
'bottom_y': None,
|
||
|
'width': None,
|
||
|
'height': None,
|
||
|
}
|
||
|
actual = find_rectangular_overlap(rect1, rect2)
|
||
|
self.assertEqual(actual, expected)
|
||
|
|
||
|
def test_no_overlap(self):
|
||
|
rect1 = {
|
||
|
'left_x': 1,
|
||
|
'bottom_y': 1,
|
||
|
'width': 2,
|
||
|
'height': 2,
|
||
|
}
|
||
|
rect2 = {
|
||
|
'left_x': 4,
|
||
|
'bottom_y': 6,
|
||
|
'width': 3,
|
||
|
'height': 6,
|
||
|
}
|
||
|
expected = {
|
||
|
'left_x': None,
|
||
|
'bottom_y': None,
|
||
|
'width': None,
|
||
|
'height': None,
|
||
|
}
|
||
|
actual = find_rectangular_overlap(rect1, rect2)
|
||
|
self.assertEqual(actual, expected)
|
||
|
|
||
|
|
||
|
unittest.main(verbosity=2)
|